summaryrefslogtreecommitdiffstats
path: root/js/src/vm/StringType.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/StringType.cpp')
-rw-r--r--js/src/vm/StringType.cpp128
1 files changed, 101 insertions, 27 deletions
diff --git a/js/src/vm/StringType.cpp b/js/src/vm/StringType.cpp
index b735b91b71..b6bd22e3d4 100644
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -359,7 +359,7 @@ const char* RepresentationToString(const JSString* s) {
template <typename KnownF, typename UnknownF>
void ForEachStringFlag(const JSString* str, uint32_t flags, KnownF known,
UnknownF unknown) {
- for (uint32_t i = js::Bit(3); i < js::Bit(16); i = i << 1) {
+ for (uint32_t i = js::Bit(3); i < js::Bit(17); i = i << 1) {
if (!(flags & i)) {
continue;
}
@@ -406,7 +406,11 @@ void ForEachStringFlag(const JSString* str, uint32_t flags, KnownF known,
known("LATIN1_CHARS_BIT");
break;
case JSString::ATOM_IS_INDEX_BIT:
- known("ATOM_IS_INDEX_BIT");
+ if (str->isAtom()) {
+ known("ATOM_IS_INDEX_BIT");
+ } else {
+ known("ATOM_REF_BIT");
+ }
break;
case JSString::INDEX_VALUE_BIT:
known("INDEX_VALUE_BIT");
@@ -418,7 +422,7 @@ void ForEachStringFlag(const JSString* str, uint32_t flags, KnownF known,
if (str->isRope()) {
known("FLATTEN_VISIT_RIGHT");
} else {
- known("NON_DEDUP_BIT");
+ known("DEPENDED_ON_BIT");
}
break;
case JSString::FLATTEN_FINISH_NODE:
@@ -429,7 +433,7 @@ void ForEachStringFlag(const JSString* str, uint32_t flags, KnownF known,
} else if (str->isAtom()) {
known("PINNED_ATOM_BIT");
} else {
- unknown(i);
+ known("NON_DEDUP_BIT");
}
break;
default:
@@ -936,6 +940,7 @@ JSLinearString* JSRope::flattenInternal(JSRope* root) {
const size_t wholeLength = root->length();
size_t wholeCapacity;
CharT* wholeChars;
+ uint32_t newRootFlags = 0;
AutoCheckCannotGC nogc;
@@ -1041,6 +1046,7 @@ finish_node: {
StringFlagsForCharType<CharT>(INIT_DEPENDENT_FLAGS));
str->d.s.u3.base =
reinterpret_cast<JSLinearString*>(root); /* will be true on exit */
+ newRootFlags |= DEPENDED_ON_BIT;
// Every interior (rope) node in the rope's tree will be visited during
// the traversal and post-barriered here, so earlier additions of
@@ -1079,10 +1085,24 @@ finish_root:
JSString& left = *leftmostChild;
RemoveCellMemory(&left, left.allocSize(), MemoryUse::StringContents);
+ // Inherit NON_DEDUP_BIT from the leftmost string.
+ newRootFlags |= left.flags() & NON_DEDUP_BIT;
+
+ // Set root's DEPENDED_ON_BIT because the leftmost string is now a
+ // dependent.
+ newRootFlags |= DEPENDED_ON_BIT;
+
uint32_t flags = INIT_DEPENDENT_FLAGS;
if (left.inStringToAtomCache()) {
flags |= IN_STRING_TO_ATOM_CACHE;
}
+ // If left was depended on, we need to make sure we preserve that. Even
+ // though the string that depended on left's buffer will now depend on
+ // root's buffer, if left is the only edge to root, replacing left with an
+ // atom ref would break that edge and allow root's buffer to be freed.
+ if (left.isDependedOn()) {
+ flags |= DEPENDED_ON_BIT;
+ }
left.setLengthAndFlags(left.length(), StringFlagsForCharType<CharT>(flags));
left.d.s.u3.base = &root->asLinear();
if (left.isTenured() && !root->isTenured()) {
@@ -1091,10 +1111,12 @@ finish_root:
// being freed (because the leftmost child may have a tenured dependent
// string that cannot be updated.)
root->storeBuffer()->putWholeCell(&left);
- root->setNonDeduplicatable();
+ newRootFlags |= NON_DEDUP_BIT;
}
}
+ root->setHeaderFlagBit(newRootFlags);
+
return &root->asLinear();
}
@@ -1477,18 +1499,17 @@ uint32_t JSAtom::getIndexSlow() const {
: AtomCharsToIndex(twoByteChars(nogc), len);
}
-// Prevent the actual owner of the string's characters from being deduplicated
-// (and thus freeing its characters, which would invalidate the ASSC's chars
-// pointer). Intermediate dependent strings on the chain can be deduplicated,
-// since the base will be updated to the root base during tenuring anyway and
-// the intermediates won't matter.
-void PreventRootBaseDeduplication(JSLinearString* s) {
+// Ensure that the incoming s.chars pointer is stable, as in, it cannot be
+// changed even across a GC. That requires that the string that owns the chars
+// not be collected or deduplicated.
+void AutoStableStringChars::holdStableChars(JSLinearString* s) {
while (s->hasBase()) {
s = s->base();
}
if (!s->isTenured()) {
s->setNonDeduplicatable();
}
+ s_ = s;
}
bool AutoStableStringChars::init(JSContext* cx, JSString* s) {
@@ -1498,6 +1519,7 @@ bool AutoStableStringChars::init(JSContext* cx, JSString* s) {
}
MOZ_ASSERT(state_ == Uninitialized);
+ length_ = linearString->length();
// Inline and nursery-allocated chars may move during a GC, so copy them
// out into a temporary malloced buffer. Note that we cannot update the
@@ -1516,9 +1538,7 @@ bool AutoStableStringChars::init(JSContext* cx, JSString* s) {
twoByteChars_ = linearString->rawTwoByteChars();
}
- PreventRootBaseDeduplication(linearString);
-
- s_ = linearString;
+ holdStableChars(linearString);
return true;
}
@@ -1529,6 +1549,7 @@ bool AutoStableStringChars::initTwoByte(JSContext* cx, JSString* s) {
}
MOZ_ASSERT(state_ == Uninitialized);
+ length_ = linearString->length();
if (linearString->hasLatin1Chars()) {
return copyAndInflateLatin1Chars(cx, linearString);
@@ -1542,9 +1563,7 @@ bool AutoStableStringChars::initTwoByte(JSContext* cx, JSString* s) {
state_ = TwoByte;
twoByteChars_ = linearString->rawTwoByteChars();
- PreventRootBaseDeduplication(linearString);
-
- s_ = linearString;
+ holdStableChars(linearString);
return true;
}
@@ -1574,16 +1593,18 @@ T* AutoStableStringChars::allocOwnChars(JSContext* cx, size_t count) {
bool AutoStableStringChars::copyAndInflateLatin1Chars(
JSContext* cx, Handle<JSLinearString*> linearString) {
- size_t length = linearString->length();
- char16_t* chars = allocOwnChars<char16_t>(cx, length);
+ MOZ_ASSERT(state_ == Uninitialized);
+ MOZ_ASSERT(s_ == nullptr);
+
+ char16_t* chars = allocOwnChars<char16_t>(cx, length_);
if (!chars) {
return false;
}
// Copy |src[0..length]| to |dest[0..length]| when copying doesn't narrow and
// therefore can't lose information.
- auto src = AsChars(Span(linearString->rawLatin1Chars(), length));
- auto dest = Span(chars, length);
+ auto src = AsChars(Span(linearString->rawLatin1Chars(), length_));
+ auto dest = Span(chars, length_);
ConvertLatin1toUtf16(src, dest);
state_ = TwoByte;
@@ -1594,13 +1615,15 @@ bool AutoStableStringChars::copyAndInflateLatin1Chars(
bool AutoStableStringChars::copyLatin1Chars(
JSContext* cx, Handle<JSLinearString*> linearString) {
- size_t length = linearString->length();
- JS::Latin1Char* chars = allocOwnChars<JS::Latin1Char>(cx, length);
+ MOZ_ASSERT(state_ == Uninitialized);
+ MOZ_ASSERT(s_ == nullptr);
+
+ JS::Latin1Char* chars = allocOwnChars<JS::Latin1Char>(cx, length_);
if (!chars) {
return false;
}
- PodCopy(chars, linearString->rawLatin1Chars(), length);
+ PodCopy(chars, linearString->rawLatin1Chars(), length_);
state_ = Latin1;
latin1Chars_ = chars;
@@ -1610,13 +1633,15 @@ bool AutoStableStringChars::copyLatin1Chars(
bool AutoStableStringChars::copyTwoByteChars(
JSContext* cx, Handle<JSLinearString*> linearString) {
- size_t length = linearString->length();
- char16_t* chars = allocOwnChars<char16_t>(cx, length);
+ MOZ_ASSERT(state_ == Uninitialized);
+ MOZ_ASSERT(s_ == nullptr);
+
+ char16_t* chars = allocOwnChars<char16_t>(cx, length_);
if (!chars) {
return false;
}
- PodCopy(chars, linearString->rawTwoByteChars(), length);
+ PodCopy(chars, linearString->rawTwoByteChars(), length_);
state_ = TwoByte;
twoByteChars_ = chars;
@@ -2501,6 +2526,55 @@ bool JSString::fillWithRepresentatives(JSContext* cx,
return true;
}
+bool JSString::tryReplaceWithAtomRef(JSAtom* atom) {
+ MOZ_ASSERT(!isAtomRef());
+
+ if (isDependedOn() || isInline() || isExternal()) {
+ return false;
+ }
+
+ AutoCheckCannotGC nogc;
+ if (hasOutOfLineChars()) {
+ void* buffer = asLinear().nonInlineCharsRaw();
+ // This is a little cheeky and so deserves a comment. If the string is
+ // not tenured, then either its buffer lives purely in the nursery, in
+ // which case it will just be forgotten and blown away in the next
+ // minor GC, or it is tracked in the nursery's mallocedBuffers hashtable,
+ // in which case it will be freed for us in the next minor GC. We opt
+ // to let the GC take care of it since there's a chance it will run
+ // during idle time.
+ if (isTenured()) {
+ RemoveCellMemory(this, allocSize(), MemoryUse::StringContents);
+ js_free(buffer);
+ }
+ }
+
+ // Pre-barrier for d.s.u3 which is overwritten and d.s.u2 which is ignored
+ // for atom refs.
+ MOZ_ASSERT(isRope() || isLinear());
+ if (isRope()) {
+ PreWriteBarrier(d.s.u2.left);
+ PreWriteBarrier(d.s.u3.right);
+ } else if (isDependent()) {
+ PreWriteBarrier(d.s.u3.base);
+ }
+
+ uint32_t flags = INIT_ATOM_REF_FLAGS;
+ d.s.u3.atom = atom;
+ if (atom->hasLatin1Chars()) {
+ flags |= LATIN1_CHARS_BIT;
+ setLengthAndFlags(length(), flags);
+ setNonInlineChars(atom->chars<Latin1Char>(nogc));
+ } else {
+ setLengthAndFlags(length(), flags);
+ setNonInlineChars(atom->chars<char16_t>(nogc));
+ }
+ // Redundant, but just a reminder that this needs to be true or else we need
+ // to check and conditionally put ourselves in the store buffer
+ MOZ_ASSERT(atom->isTenured());
+ return true;
+}
+
/*** Conversions ************************************************************/
UniqueChars js::EncodeLatin1(JSContext* cx, JSString* str) {