diff options
Diffstat (limited to 'js/src/vm/StringType.cpp')
-rw-r--r-- | js/src/vm/StringType.cpp | 128 |
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) { |