mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1013917 part 1 - Some JSString changes for latin1 strings. r=luke
This commit is contained in:
parent
d697174be6
commit
5096a71998
@ -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]
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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'))
|
||||
|
Loading…
Reference in New Issue
Block a user