Bug 1013917 part 1 - Some JSString changes for latin1 strings. r=luke

This commit is contained in:
Jan de Mooij 2014-05-22 21:40:34 +02:00
parent d697174be6
commit 5096a71998
10 changed files with 170 additions and 85 deletions

View File

@ -19,6 +19,8 @@ class JSStringTypeCache(object):
self.ROPE_FLAGS = dummy['ROPE_FLAGS']
self.ATOM_BIT = dummy['ATOM_BIT']
self.INLINE_CHARS_BIT = dummy['INLINE_CHARS_BIT']
self.TYPE_FLAGS_MASK = dummy['TYPE_FLAGS_MASK']
self.LATIN1_CHARS_BIT = dummy['LATIN1_CHARS_BIT']
class Common(mozilla.prettyprinters.Pointer):
def __init__(self, value, cache):
@ -36,7 +38,7 @@ class JSStringPtr(Common):
d = self.value['d']
length = d['u1']['length']
flags = d['u1']['flags']
is_rope = (flags == self.stc.ROPE_FLAGS)
is_rope = ((flags & self.stc.TYPE_FLAGS_MASK) == self.stc.ROPE_FLAGS)
if is_rope:
for c in JSStringPtr(d['s']['u2']['left'], self.cache).jschars():
yield c
@ -44,10 +46,17 @@ class JSStringPtr(Common):
yield c
else:
is_inline = (flags & self.stc.INLINE_CHARS_BIT) != 0
is_latin1 = (flags & self.stc.LATIN1_CHARS_BIT) != 0
if is_inline:
chars = d['inlineStorage']
if is_latin1:
chars = d['inlineStorageLatin1']
else:
chars = d['inlineStorageTwoByte']
else:
chars = d['s']['u2']['nonInlineChars']
if is_latin1:
chars = d['s']['u2']['nonInlineCharsLatin1']
else:
chars = d['s']['u2']['nonInlineCharsTwoByte']
for i in range(length):
yield chars[i]

View File

@ -5154,7 +5154,7 @@ JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
// Check if we can use a JSFatInlineString.
Label isFatInline;
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_FAT_INLINE_LENGTH),
masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
&isFatInline);
// Ensure result length <= JSString::MAX_LENGTH.

View File

@ -384,7 +384,8 @@ class MacroAssembler : public MacroAssemblerSpecific
void branchIfRope(Register str, Label *label) {
Address flags(str, JSString::offsetOfFlags());
branch32(Assembler::Equal, flags, Imm32(JSString::ROPE_FLAGS), label);
static_assert(JSString::ROPE_FLAGS == 0, "Rope type flags must be 0");
branchTest32(Assembler::Zero, flags, Imm32(JSString::TYPE_FLAGS_MASK), label);
}
void loadSliceBounds(Register worker, Register dest) {

View File

@ -587,11 +587,14 @@ struct Function {
struct Atom {
static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
uint32_t flags;
uint32_t length;
union {
const jschar *nonInlineChars;
char inlineStorage[1];
const char *nonInlineCharsLatin1;
const jschar *nonInlineCharsTwoByte;
char inlineStorageLatin1[1];
jschar inlineStorageTwoByte[1];
};
};
@ -773,11 +776,12 @@ GetAtomChars(JSAtom *atom)
{
using shadow::Atom;
Atom *atom_ = reinterpret_cast<Atom *>(atom);
JS_ASSERT(!(atom_->flags & Atom::LATIN1_CHARS_BIT));
if (atom_->flags & Atom::INLINE_CHARS_BIT) {
char *p = reinterpret_cast<char *>(atom);
return reinterpret_cast<const jschar *>(p + offsetof(Atom, inlineStorage));
return reinterpret_cast<const jschar *>(p + offsetof(Atom, inlineStorageTwoByte));
}
return atom_->nonInlineChars;
return atom_->nonInlineCharsTwoByte;
}
inline size_t

View File

@ -600,10 +600,10 @@ js::Int32ToString(ThreadSafeContext *cx, int32_t si)
if (!str)
return nullptr;
jschar buffer[JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1];
jschar buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
size_t length;
jschar *start = BackfillInt32InBuffer(si, buffer,
JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1, &length);
JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
PodCopy(str->init(length), start, length + 1);
@ -623,9 +623,9 @@ js::Int32ToAtom(ExclusiveContext *cx, int32_t si)
if (JSFlatString *str = LookupInt32ToString(cx, si))
return js::AtomizeString(cx, str);
char buffer[JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1];
char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
size_t length;
char *start = BackfillInt32InBuffer(si, buffer, JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1, &length);
char *start = BackfillInt32InBuffer(si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
JSAtom *atom = Atomize(cx, start, length);
if (!atom)
@ -1430,9 +1430,9 @@ js::IndexToString(JSContext *cx, uint32_t index)
if (!str)
return nullptr;
jschar buffer[JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1];
RangedPtr<jschar> end(buffer + JSFatInlineString::MAX_FAT_INLINE_LENGTH,
buffer, JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1);
jschar buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
RangedPtr<jschar> end(buffer + JSFatInlineString::MAX_LENGTH_TWO_BYTE,
buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1);
*end = '\0';
RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);

View File

@ -2775,7 +2775,7 @@ static inline JSFatInlineString *
FlattenSubstrings(JSContext *cx, const jschar *chars,
const StringRange *ranges, size_t rangesLen, size_t outputLen)
{
JS_ASSERT(JSFatInlineString::lengthFits(outputLen));
JS_ASSERT(JSFatInlineString::twoByteLengthFits(outputLen));
JSFatInlineString *str = js_NewGCFatInlineString<CanGC>(cx);
if (!str)
@ -2817,7 +2817,7 @@ AppendSubstrings(JSContext *cx, Handle<JSFlatString*> flatStr,
size_t substrLen = 0;
size_t end = i;
for (; end < rangesLen; end++) {
if (substrLen + ranges[end].length > JSFatInlineString::MAX_FAT_INLINE_LENGTH)
if (substrLen + ranges[end].length > JSFatInlineString::MAX_LENGTH_TWO_BYTE)
break;
substrLen += ranges[end].length;
}
@ -4179,7 +4179,7 @@ template <AllowGC allowGC>
JSFlatString *
js_NewStringCopyN(ExclusiveContext *cx, const jschar *s, size_t n)
{
if (JSFatInlineString::lengthFits(n))
if (JSFatInlineString::twoByteLengthFits(n))
return NewFatInlineString<allowGC>(cx, TwoByteChars(s, n));
jschar *news = cx->pod_malloc<jschar>(n + 1);
@ -4203,7 +4203,7 @@ template <AllowGC allowGC>
JSFlatString *
js_NewStringCopyN(ThreadSafeContext *cx, const char *s, size_t n)
{
if (JSFatInlineString::lengthFits(n))
if (JSFatInlineString::twoByteLengthFits(n))
return NewFatInlineString<allowGC>(cx, JS::Latin1Chars(s, n));
jschar *chars = InflateString(cx, s, &n);
@ -4226,7 +4226,7 @@ JSFlatString *
js_NewStringCopyZ(ExclusiveContext *cx, const jschar *s)
{
size_t n = js_strlen(s);
if (JSFatInlineString::lengthFits(n))
if (JSFatInlineString::twoByteLengthFits(n))
return NewFatInlineString<allowGC>(cx, TwoByteChars(s, n));
size_t m = (n + 1) * sizeof(jschar);

View File

@ -24,11 +24,11 @@ static MOZ_ALWAYS_INLINE JSInlineString *
NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars)
{
size_t len = chars.length();
JS_ASSERT(JSFatInlineString::lengthFits(len));
JS_ASSERT(JSFatInlineString::twoByteLengthFits(len));
JSInlineString *str;
jschar *p;
if (JSInlineString::lengthFits(len)) {
if (JSInlineString::twoByteLengthFits(len)) {
str = JSInlineString::new_<allowGC>(cx);
if (!str)
return nullptr;
@ -57,11 +57,11 @@ NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars)
* Don't bother trying to find a static atom; measurement shows that not
* many get here (for one, Atomize is catching them).
*/
JS_ASSERT(JSFatInlineString::lengthFits(len));
JS_ASSERT(JSFatInlineString::twoByteLengthFits(len));
JSInlineString *str;
jschar *storage;
if (JSInlineString::lengthFits(len)) {
if (JSInlineString::twoByteLengthFits(len)) {
str = JSInlineString::new_<allowGC>(cx);
if (!str)
return nullptr;
@ -143,7 +143,7 @@ JSDependentString::init(js::ThreadSafeContext *cx, JSLinearString *base, const j
JS_ASSERT(!js::IsPoisonedPtr(base));
d.u1.length = length;
d.u1.flags = DEPENDENT_FLAGS;
d.s.u2.nonInlineChars = chars;
d.s.u2.nonInlineCharsTwoByte = chars;
d.s.u3.base = base;
js::StringWriteBarrierPost(cx, reinterpret_cast<JSString **>(&d.s.u3.base));
}
@ -177,7 +177,7 @@ JSDependentString::new_(js::ExclusiveContext *cx,
* both to avoid the awkward moving-GC hazard this introduces and because it
* is more efficient to immediately undepend here.
*/
if (JSFatInlineString::lengthFits(length))
if (JSFatInlineString::twoByteLengthFits(length))
return js::NewFatInlineString<js::CanGC>(cx, JS::TwoByteChars(chars, length));
JSDependentString *str = (JSDependentString *)js_NewGCString<js::NoGC>(cx);
@ -207,7 +207,7 @@ JSFlatString::init(const jschar *chars, size_t length)
{
d.u1.length = length;
d.u1.flags = FLAT_BIT;
d.s.u2.nonInlineChars = chars;
d.s.u2.nonInlineCharsTwoByte = chars;
}
template <js::AllowGC allowGC>
@ -250,19 +250,19 @@ JSInlineString::new_(js::ThreadSafeContext *cx)
MOZ_ALWAYS_INLINE jschar *
JSInlineString::init(size_t length)
{
JS_ASSERT(lengthFits(length));
JS_ASSERT(twoByteLengthFits(length));
d.u1.length = length;
d.u1.flags = INIT_INLINE_FLAGS;
return d.inlineStorage;
return d.inlineStorageTwoByte;
}
MOZ_ALWAYS_INLINE jschar *
JSFatInlineString::init(size_t length)
{
JS_ASSERT(lengthFits(length));
JS_ASSERT(twoByteLengthFits(length));
d.u1.length = length;
d.u1.flags = INIT_FAT_INLINE_FLAGS;
return d.inlineStorage;
return d.inlineStorageTwoByte;
}
template <js::AllowGC allowGC>
@ -279,7 +279,7 @@ JSExternalString::init(const jschar *chars, size_t length, const JSStringFinaliz
JS_ASSERT(fin->finalize);
d.u1.length = length;
d.u1.flags = EXTERNAL_FLAGS;
d.s.u2.nonInlineChars = chars;
d.s.u2.nonInlineCharsTwoByte = chars;
d.s.u3.externalFinalizer = fin;
}

View File

@ -269,7 +269,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
}
JSString *child = str->d.s.u2.left;
JS_ASSERT(child->isRope());
str->d.s.u2.nonInlineChars = left.nonInlineChars();
str->d.s.u2.nonInlineCharsTwoByte = left.nonInlineChars();
child->d.u1.flattenData = uintptr_t(str) | Tag_VisitRightChild;
str = child;
}
@ -277,7 +277,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
JSString::writeBarrierPre(str->d.s.u2.left);
JSString::writeBarrierPre(str->d.s.u3.right);
}
str->d.s.u2.nonInlineChars = left.nonInlineChars();
str->d.s.u2.nonInlineCharsTwoByte = left.nonInlineChars();
wholeCapacity = capacity;
wholeChars = const_cast<jschar *>(left.nonInlineChars());
pos = wholeChars + left.d.u1.length;
@ -301,7 +301,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
}
JSString &left = *str->d.s.u2.left;
str->d.s.u2.nonInlineChars = pos;
str->d.s.u2.nonInlineCharsTwoByte = pos;
StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
if (left.isRope()) {
/* Return to this node when 'left' done, then goto visit_right_child. */
@ -331,7 +331,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
*pos = '\0';
str->d.u1.length = wholeLength;
str->d.u1.flags = EXTENSIBLE_FLAGS;
str->d.s.u2.nonInlineChars = wholeChars;
str->d.s.u2.nonInlineCharsTwoByte = wholeChars;
str->d.s.u3.capacity = wholeCapacity;
StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
StringWriteBarrierPostRemove(maybecx, &str->d.s.u3.right);
@ -339,7 +339,7 @@ JSRope::flattenInternal(ExclusiveContext *maybecx)
}
uintptr_t flattenData = str->d.u1.flattenData;
str->d.u1.flags = DEPENDENT_FLAGS;
str->d.u1.length = pos - str->d.s.u2.nonInlineChars;
str->d.u1.length = pos - str->d.s.u2.nonInlineCharsTwoByte;
str->d.s.u3.base = (JSLinearString *)this; /* will be true on exit */
StringWriteBarrierPost(maybecx, (JSString **)&str->d.s.u3.base);
str = (JSString *)(flattenData & ~Tag_Mask);
@ -384,7 +384,7 @@ js::ConcatStrings(ThreadSafeContext *cx,
if (!JSString::validateLength(cx, wholeLength))
return nullptr;
if (JSFatInlineString::lengthFits(wholeLength) && cx->isJSContext()) {
if (JSFatInlineString::twoByteLengthFits(wholeLength) && cx->isJSContext()) {
JSFatInlineString *str = js_NewGCFatInlineString<allowGC>(cx);
if (!str)
return nullptr;
@ -448,7 +448,7 @@ JSDependentString::undepend(ExclusiveContext *cx)
PodCopy(s, nonInlineChars(), n);
s[n] = 0;
d.s.u2.nonInlineChars = s;
d.s.u2.nonInlineCharsTwoByte = s;
/*
* Transform *this into an undepended string so 'base' will remain rooted

View File

@ -79,17 +79,22 @@ static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
* string (JSExternalString) can be created whose chars are managed by the
* JSAPI client.
*
* - To avoid using two bytes per character for every string, string characters
* are stored as Latin1 instead of TwoByte if all characters are representable
* in Latin1. Note that Latin1 strings are not yet enabled by default, see
* bug 998392.
*
* Although all strings share the same basic memory layout, we can conceptually
* arrange them into a hierarchy of operations/invariants and represent this
* hierarchy in C++ with classes:
*
* C++ type operations+fields / invariants+properties
* ========================== =========================================
* JSString (abstract) getCharsZ, getChars, length / -
* JSString (abstract) get(Latin1|TwoByte)CharsZ, get(Latin1|TwoByte)Chars, length / -
* | \
* | JSRope leftChild, rightChild / -
* |
* JSLinearString (abstract) chars / might be null-terminated
* JSLinearString (abstract) latin1Chars, twoByteChars / might be null-terminated
* | \
* | JSDependentString base / -
* |
@ -131,7 +136,8 @@ static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
class JSString : public js::gc::BarrieredCell<JSString>
{
protected:
static const size_t NUM_INLINE_CHARS = 2 * sizeof(void *) / sizeof(jschar);
static const size_t NUM_INLINE_CHARS_LATIN1 = 2 * sizeof(void *) / sizeof(char);
static const size_t NUM_INLINE_CHARS_TWO_BYTE = 2 * sizeof(void *) / sizeof(jschar);
/* Fields only apply to string types commented on the right. */
struct Data
@ -144,10 +150,15 @@ class JSString : public js::gc::BarrieredCell<JSString>
uintptr_t flattenData; /* JSRope (temporary while flattening) */
} u1;
union {
jschar inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|FatInline)String */
union {
/* JS(Fat)InlineString */
char inlineStorageLatin1[NUM_INLINE_CHARS_LATIN1];
jschar inlineStorageTwoByte[NUM_INLINE_CHARS_TWO_BYTE];
};
struct {
union {
const jschar *nonInlineChars; /* JSLinearString, except JS(Inline|FatInline)String */
const char *nonInlineCharsLatin1; /* JSLinearString, except JS(Fat)InlineString */
const jschar *nonInlineCharsTwoByte;/* JSLinearString, except JS(Fat)InlineString */
JSString *left; /* JSRope */
} u2;
union {
@ -164,10 +175,20 @@ class JSString : public js::gc::BarrieredCell<JSString>
/* Flags exposed only for jits */
/*
* Instead of using a dense index to represent the most-derived type, string
* types are encoded to allow single-op tests for hot queries (isRope,
* isDependent, isFlat, isAtom) which, in view of subtyping, would require
* slower (isX() || isY() || isZ()).
* The Flags Word
*
* The flags word stores both the string's type and its character encoding.
*
* If LATIN1_CHARS_BIT is set, the string's characters are stored as Latin1
* instead of TwoByte. This flag can also be set for ropes, if both the
* left and right nodes are Latin1. Flattening will result in a Latin1
* string in this case.
*
* The other flags store the string's type. Instead of using a dense index
* to represent the most-derived type, string types are encoded to allow
* single-op tests for hot queries (isRope, isDependent, isFlat, isAtom)
* which, in view of subtyping, would require slower
* (isX() || isY() || isZ()).
*
* The string type encoding can be summarized as follows. The "instance
* encoding" entry for a type specifies the flag bits used to create a
@ -228,6 +249,10 @@ class JSString : public js::gc::BarrieredCell<JSString>
static const uint32_t INIT_INLINE_FLAGS = FLAT_BIT | INLINE_CHARS_BIT;
static const uint32_t INIT_FAT_INLINE_FLAGS = FLAT_BIT | FAT_INLINE_MASK;
static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
static const uint32_t MAX_LENGTH = JS_BIT(28) - 1;
/*
@ -240,8 +265,13 @@ class JSString : public js::gc::BarrieredCell<JSString>
static void staticAsserts() {
static_assert(JSString::MAX_LENGTH < UINT32_MAX, "Length must fit in 32 bits");
static_assert(sizeof(JSString) ==
offsetof(JSString, d.inlineStorage) + NUM_INLINE_CHARS * sizeof(jschar),
"NUM_INLINE_CHARS inline chars must fit in a JSString");
(offsetof(JSString, d.inlineStorageLatin1) +
NUM_INLINE_CHARS_LATIN1 * sizeof(char)),
"Inline chars must fit in a JSString");
static_assert(sizeof(JSString) ==
(offsetof(JSString, d.inlineStorageTwoByte) +
NUM_INLINE_CHARS_TWO_BYTE * sizeof(jschar)),
"Inline chars must fit in a JSString");
/* Ensure js::shadow::Atom has the same layout. */
using js::shadow::Atom;
@ -249,12 +279,18 @@ class JSString : public js::gc::BarrieredCell<JSString>
"shadow::Atom length offset must match JSString");
static_assert(offsetof(JSString, d.u1.flags) == offsetof(Atom, flags),
"shadow::Atom flags offset must match JSString");
static_assert(offsetof(JSString, d.s.u2.nonInlineChars) == offsetof(Atom, nonInlineChars),
static_assert(offsetof(JSString, d.s.u2.nonInlineCharsLatin1) == offsetof(Atom, nonInlineCharsLatin1),
"shadow::Atom nonInlineChars offset must match JSString");
static_assert(offsetof(JSString, d.inlineStorage) == offsetof(Atom, inlineStorage),
static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) == offsetof(Atom, nonInlineCharsTwoByte),
"shadow::Atom nonInlineChars offset must match JSString");
static_assert(offsetof(JSString, d.inlineStorageLatin1) == offsetof(Atom, inlineStorageLatin1),
"shadow::Atom inlineStorage offset must match JSString");
static_assert(offsetof(JSString, d.inlineStorageTwoByte) == offsetof(Atom, inlineStorageTwoByte),
"shadow::Atom inlineStorage offset must match JSString");
static_assert(INLINE_CHARS_BIT == Atom::INLINE_CHARS_BIT,
"shadow::Atom::INLINE_CHARS_BIT must match JSString::INLINE_CHARS_BIT");
static_assert(LATIN1_CHARS_BIT == Atom::LATIN1_CHARS_BIT,
"shadow::Atom::LATIN1_CHARS_BIT must match JSString::LATIN1_CHARS_BIT");
}
/* Avoid lame compile errors in JSRope::flatten */
@ -299,6 +335,14 @@ class JSString : public js::gc::BarrieredCell<JSString>
inline bool copyNonPureCharsZ(js::ThreadSafeContext *cx,
js::ScopedJSFreePtr<jschar> &out) const;
/* Strings have either Latin1 or TwoByte chars. */
bool hasLatin1Chars() const {
return d.u1.flags & LATIN1_CHARS_BIT;
}
bool hasTwoByteChars() const {
return !(d.u1.flags & LATIN1_CHARS_BIT);
}
/* Fallible conversions to more-derived string types. */
inline JSLinearString *ensureLinear(js::ExclusiveContext *cx);
@ -312,7 +356,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
MOZ_ALWAYS_INLINE
bool isRope() const {
return d.u1.flags == ROPE_FLAGS;
return (d.u1.flags & TYPE_FLAGS_MASK) == ROPE_FLAGS;
}
MOZ_ALWAYS_INLINE
@ -334,7 +378,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
MOZ_ALWAYS_INLINE
bool isDependent() const {
return d.u1.flags == DEPENDENT_FLAGS;
return (d.u1.flags & TYPE_FLAGS_MASK) == DEPENDENT_FLAGS;
}
MOZ_ALWAYS_INLINE
@ -356,7 +400,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
MOZ_ALWAYS_INLINE
bool isExtensible() const {
return d.u1.flags == EXTENSIBLE_FLAGS;
return (d.u1.flags & TYPE_FLAGS_MASK) == EXTENSIBLE_FLAGS;
}
MOZ_ALWAYS_INLINE
@ -383,7 +427,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
/* For hot code, prefer other type queries. */
bool isExternal() const {
return d.u1.flags == EXTERNAL_FLAGS;
return (d.u1.flags & TYPE_FLAGS_MASK) == EXTERNAL_FLAGS;
}
MOZ_ALWAYS_INLINE
@ -394,7 +438,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
MOZ_ALWAYS_INLINE
bool isUndepended() const {
return d.u1.flags == UNDEPENDED_FLAGS;
return (d.u1.flags & TYPE_FLAGS_MASK) == UNDEPENDED_FLAGS;
}
MOZ_ALWAYS_INLINE
@ -441,7 +485,7 @@ class JSString : public js::gc::BarrieredCell<JSString>
}
static size_t offsetOfNonInlineChars() {
return offsetof(JSString, d.s.u2.nonInlineChars);
return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte);
}
js::gc::AllocKind getAllocKind() const { return tenuredGetAllocKind(); }
@ -537,7 +581,8 @@ class JSLinearString : public JSString
MOZ_ALWAYS_INLINE
const jschar *nonInlineChars() const {
JS_ASSERT(!isInline());
return d.s.u2.nonInlineChars;
JS_ASSERT(hasTwoByteChars());
return d.s.u2.nonInlineCharsTwoByte;
}
MOZ_ALWAYS_INLINE
@ -652,10 +697,15 @@ class JSExtensibleString : public JSFlatString
JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
/* On 32-bit platforms, MAX_INLINE_LENGTH is 4. On 64-bit platforms it is 8. */
/*
* On 32-bit platforms, JSInlineString can store 7 Latin1 characters or
* 3 TwoByte characters (excluding null terminator) inline. On 64-bit
* platforms, these numbers are 15 and 7, respectively.
*/
class JSInlineString : public JSFlatString
{
static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
static const size_t MAX_LENGTH_LATIN1 = NUM_INLINE_CHARS_LATIN1 - 1;
static const size_t MAX_LENGTH_TWO_BYTE = NUM_INLINE_CHARS_TWO_BYTE - 1;
/* Hide chars(), inlineChars() is more efficient. */
const jschar *chars() const MOZ_DELETE;
@ -670,61 +720,81 @@ class JSInlineString : public JSFlatString
MOZ_ALWAYS_INLINE
const jschar *inlineChars() const {
JS_ASSERT(hasTwoByteChars());
const char *p = reinterpret_cast<const char *>(this);
return reinterpret_cast<const jschar *>(p + offsetOfInlineStorage());
}
static bool lengthFits(size_t length) {
return length <= MAX_INLINE_LENGTH;
static bool latin1LengthFits(size_t length) {
return length <= MAX_LENGTH_LATIN1;
}
static bool twoByteLengthFits(size_t length) {
return length <= MAX_LENGTH_TWO_BYTE;
}
static size_t offsetOfInlineStorage() {
return offsetof(JSInlineString, d.inlineStorage);
return offsetof(JSInlineString, d.inlineStorageTwoByte);
}
};
JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
/*
* On both 32-bit and 64-bit platforms, INLINE_EXTENSION_CHARS is 12. This is
* deliberate, in order to minimize potential performance differences between
* 32-bit and 64-bit platforms.
* On both 32-bit and 64-bit platforms, MAX_LENGTH_TWO_BYTE is 11 and
* MAX_LENGTH_LATIN1 is 23 (excluding null terminator). This is deliberate,
* in order to minimize potential performance differences between 32-bit and
* 64-bit platforms.
*
* There are still some differences due to NUM_INLINE_CHARS being different.
* E.g. strings of length 4--7 will be JSFatInlineStrings on 32-bit platforms
* and JSInlineStrings on 64-bit platforms. But the more significant transition
* from inline strings to non-inline strings occurs at length 12 on both 32-bit
* and 64-bit platforms.
* There are still some differences due to NUM_INLINE_CHARS_* being different.
* E.g. TwoByte strings of length 4--7 will be JSFatInlineStrings on 32-bit
* platforms and JSInlineStrings on 64-bit platforms. But the more significant
* transition from inline strings to non-inline strings occurs at length 11 (for
* TwoByte strings) and 23 (Latin1 strings) on both 32-bit and 64-bit platforms.
*/
class JSFatInlineString : public JSInlineString
{
static const size_t INLINE_EXTENSION_CHARS = 12 - NUM_INLINE_CHARS;
static const size_t INLINE_EXTENSION_CHARS_LATIN1 = 24 - NUM_INLINE_CHARS_LATIN1;
static const size_t INLINE_EXTENSION_CHARS_TWO_BYTE = 12 - NUM_INLINE_CHARS_TWO_BYTE;
static void staticAsserts() {
JS_STATIC_ASSERT((INLINE_EXTENSION_CHARS * sizeof(jschar)) % js::gc::CellSize == 0);
JS_STATIC_ASSERT(MAX_FAT_INLINE_LENGTH + 1 ==
JS_STATIC_ASSERT((INLINE_EXTENSION_CHARS_LATIN1 * sizeof(char)) % js::gc::CellSize == 0);
JS_STATIC_ASSERT((INLINE_EXTENSION_CHARS_TWO_BYTE * sizeof(jschar)) % js::gc::CellSize == 0);
JS_STATIC_ASSERT(MAX_LENGTH_TWO_BYTE + 1 ==
(sizeof(JSFatInlineString) -
offsetof(JSFatInlineString, d.inlineStorage)) / sizeof(jschar));
offsetof(JSFatInlineString, d.inlineStorageTwoByte)) / sizeof(jschar));
JS_STATIC_ASSERT(MAX_LENGTH_LATIN1 + 1 ==
(sizeof(JSFatInlineString) -
offsetof(JSFatInlineString, d.inlineStorageLatin1)) / sizeof(char));
}
/* Hide chars(), inlineChars() is more efficient. */
const jschar *chars() const MOZ_DELETE;
protected: /* to fool clang into not warning this is unused */
jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
union {
char inlineStorageExtensionLatin1[INLINE_EXTENSION_CHARS_LATIN1];
jschar inlineStorageExtensionTwoByte[INLINE_EXTENSION_CHARS_TWO_BYTE];
};
public:
template <js::AllowGC allowGC>
static inline JSFatInlineString *new_(js::ThreadSafeContext *cx);
static const size_t MAX_FAT_INLINE_LENGTH = JSString::NUM_INLINE_CHARS +
INLINE_EXTENSION_CHARS
-1 /* null terminator */;
static const size_t MAX_LENGTH_LATIN1 = JSString::NUM_INLINE_CHARS_LATIN1 +
INLINE_EXTENSION_CHARS_LATIN1
-1 /* null terminator */;
static const size_t MAX_LENGTH_TWO_BYTE = JSString::NUM_INLINE_CHARS_TWO_BYTE +
INLINE_EXTENSION_CHARS_TWO_BYTE
-1 /* null terminator */;
inline jschar *init(size_t length);
static bool lengthFits(size_t length) {
return length <= MAX_FAT_INLINE_LENGTH;
static bool latin1LengthFits(size_t length) {
return length <= MAX_LENGTH_LATIN1;
}
static bool twoByteLengthFits(size_t length) {
return length <= MAX_LENGTH_TWO_BYTE;
}
/* Only called by the GC for strings with the FINALIZE_FAT_INLINE_STRING kind. */
@ -1103,6 +1173,7 @@ MOZ_ALWAYS_INLINE const jschar *
JSLinearString::chars() const
{
JS_ASSERT(JSString::isLinear());
JS_ASSERT(hasTwoByteChars());
return isInline() ? asInline().inlineChars() : nonInlineChars();
}

View File

@ -49,8 +49,8 @@ StringBuffer::finishString()
if (!JSString::validateLength(cx, length))
return nullptr;
JS_STATIC_ASSERT(JSFatInlineString::MAX_FAT_INLINE_LENGTH < CharBuffer::InlineLength);
if (JSFatInlineString::lengthFits(length))
JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_TWO_BYTE < CharBuffer::InlineLength);
if (JSFatInlineString::twoByteLengthFits(length))
return NewFatInlineString<CanGC>(cx, TwoByteChars(cb.begin(), length));
if (!cb.append('\0'))