summaryrefslogtreecommitdiffstats
path: root/js/src/vm/StringType.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/vm/StringType.h148
1 files changed, 115 insertions, 33 deletions
diff --git a/js/src/vm/StringType.h b/js/src/vm/StringType.h
index 38dea85c60..4073c45c70 100644
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -147,6 +147,8 @@ bool CheckStringIsIndex(const CharT* s, size_t length, uint32_t* indexp);
* JSLinearString latin1Chars, twoByteChars / -
* |
* +-- JSDependentString base / -
+ * | |
+ * | +-- JSAtomRefString - / base points to an atom
* |
* +-- JSExternalString - / char array memory managed by embedding
* |
@@ -275,6 +277,7 @@ class JSString : public js::gc::CellWithLengthAndFlags {
} u2;
union {
JSLinearString* base; /* JSDependentString */
+ JSAtom* atom; /* JSAtomRefString */
JSString* right; /* JSRope */
size_t capacity; /* JSLinearString (extensible) */
const JSExternalStringCallbacks*
@@ -317,28 +320,30 @@ class JSString : public js::gc::CellWithLengthAndFlags {
* String Instance Subtype
* type encoding predicate
* -----------------------------------------
- * Rope 000000 000 xxxx0x xxx
- * Linear 000010 000 xxxx1x xxx
- * Dependent 000110 000 xxx1xx xxx
- * External 100010 000 100010 xxx
- * Extensible 010010 000 010010 xxx
- * Inline 001010 000 xx1xxx xxx
- * FatInline 011010 000 x11xxx xxx
- * JSAtom - xxxxx1 xxx
- * NormalAtom 000011 000 xx0xx1 xxx
- * PermanentAtom 100011 000 1xxxx1 xxx
- * ThinInlineAtom 001011 000 x01xx1 xxx
- * FatInlineAtom 011011 000 x11xx1 xxx
- * |||||| |||
- * |||||| ||\- [0] reserved (FORWARD_BIT)
- * |||||| |\-- [1] reserved
- * |||||| \--- [2] reserved
- * |||||\----- [3] IsAtom
- * ||||\------ [4] IsLinear
- * |||\------- [5] IsDependent
- * ||\-------- [6] IsInline
- * |\--------- [7] FatInlineAtom/Extensible
- * \---------- [8] External/Permanent
+ * Rope 0000000 000 xxxxx0x xxx
+ * Linear 0000010 000 xxxxx1x xxx
+ * Dependent 0000110 000 xxxx1xx xxx
+ * AtomRef 1000110 000 1xxxxxx xxx
+ * External 0100010 000 x100010 xxx
+ * Extensible 0010010 000 x010010 xxx
+ * Inline 0001010 000 xxx1xxx xxx
+ * FatInline 0011010 000 xx11xxx xxx
+ * JSAtom - xxxxxx1 xxx
+ * NormalAtom 0000011 000 xxx0xx1 xxx
+ * PermanentAtom 0100011 000 x1xxxx1 xxx
+ * ThinInlineAtom 0001011 000 xx01xx1 xxx
+ * FatInlineAtom 0011011 000 xx11xx1 xxx
+ * ||||||| |||
+ * ||||||| ||\- [0] reserved (FORWARD_BIT)
+ * ||||||| |\-- [1] reserved
+ * ||||||| \--- [2] reserved
+ * ||||||\----- [3] IsAtom
+ * |||||\------ [4] IsLinear
+ * ||||\------- [5] IsDependent
+ * |||\-------- [6] IsInline
+ * ||\--------- [7] FatInlineAtom/Extensible
+ * |\---------- [8] External/Permanent
+ * \----------- [9] AtomRef
*
* Bits 0..2 are reserved for use by the GC (see
* gc::CellFlagBitsReservedForGC). In particular, bit 0 is currently used for
@@ -364,6 +369,8 @@ class JSString : public js::gc::CellWithLengthAndFlags {
static const uint32_t LINEAR_BIT = js::Bit(4);
static const uint32_t DEPENDENT_BIT = js::Bit(5);
static const uint32_t INLINE_CHARS_BIT = js::Bit(6);
+ // Indicates a dependent string pointing to an atom
+ static const uint32_t ATOM_REF_BIT = js::Bit(9);
static const uint32_t LINEAR_IS_EXTENSIBLE_BIT = js::Bit(7);
static const uint32_t INLINE_IS_FAT_BIT = js::Bit(7);
@@ -383,11 +390,19 @@ class JSString : public js::gc::CellWithLengthAndFlags {
static const uint32_t INIT_ROPE_FLAGS = 0;
static const uint32_t INIT_LINEAR_FLAGS = LINEAR_BIT;
static const uint32_t INIT_DEPENDENT_FLAGS = LINEAR_BIT | DEPENDENT_BIT;
+ static const uint32_t INIT_ATOM_REF_FLAGS =
+ INIT_DEPENDENT_FLAGS | ATOM_REF_BIT;
- static const uint32_t TYPE_FLAGS_MASK = js::BitMask(9) - js::BitMask(3);
+ static const uint32_t TYPE_FLAGS_MASK = js::BitMask(10) - js::BitMask(3);
static_assert((TYPE_FLAGS_MASK & js::gc::HeaderWord::RESERVED_MASK) == 0,
"GC reserved bits must not be used for Strings");
+ // Whether this atom's characters store an uint32 index value less than or
+ // equal to MAX_ARRAY_INDEX. This bit means something different if the
+ // string is not an atom (see ATOM_REF_BIT)
+ // See JSLinearString::isIndex.
+ static const uint32_t ATOM_IS_INDEX_BIT = js::Bit(9);
+
// Linear strings:
// - Content and representation are Latin-1 characters.
// - Unmodifiable after construction.
@@ -397,12 +412,7 @@ class JSString : public js::gc::CellWithLengthAndFlags {
// - Flag may be cleared when the rope is changed into a dependent string.
//
// Also see LATIN1_CHARS_BIT description under "Flag Encoding".
- static const uint32_t LATIN1_CHARS_BIT = js::Bit(9);
-
- // Whether this atom's characters store an uint32 index value less than or
- // equal to MAX_ARRAY_INDEX. Not used for non-atomized strings.
- // See JSLinearString::isIndex.
- static const uint32_t ATOM_IS_INDEX_BIT = js::Bit(10);
+ static const uint32_t LATIN1_CHARS_BIT = js::Bit(10);
static const uint32_t INDEX_VALUE_BIT = js::Bit(11);
static const uint32_t INDEX_VALUE_SHIFT = 16;
@@ -424,6 +434,11 @@ class JSString : public js::gc::CellWithLengthAndFlags {
static const uint32_t FLATTEN_MASK =
FLATTEN_VISIT_RIGHT | FLATTEN_FINISH_NODE;
+ // Indicates that this string is depended on by another string. A rope should
+ // never be depended on, and this should never be set during flattening, so
+ // we can reuse the FLATTEN_VISIT_RIGHT bit.
+ static const uint32_t DEPENDED_ON_BIT = FLATTEN_VISIT_RIGHT;
+
static const uint32_t PINNED_ATOM_BIT = js::Bit(15);
static const uint32_t PERMANENT_ATOM_MASK =
ATOM_BIT | PINNED_ATOM_BIT | ATOM_IS_PERMANENT_BIT;
@@ -543,6 +558,35 @@ class JSString : public js::gc::CellWithLengthAndFlags {
return flags() >> INDEX_VALUE_SHIFT;
}
+ /*
+ * Whether any dependent strings point to this string's chars. This is needed
+ * so that we don't replace the string with a forwarded atom and free its
+ * buffer.
+ *
+ * NOTE: we specifically do not set this for atoms, because they are accessed
+ * on many threads and we don't want to mess with their flags if we don't
+ * have to, and it is safe because atoms will never be replaced by an atom
+ * ref.
+ */
+ bool isDependedOn() const {
+ bool result = flags() & DEPENDED_ON_BIT;
+ MOZ_ASSERT_IF(result, !isRope() && !isAtom());
+ return result;
+ }
+
+ bool assertIsValidBase() const {
+ // See isDependedOn comment for why we're excluding atoms
+ return isAtom() || isDependedOn();
+ }
+
+ void setDependedOn() {
+ MOZ_ASSERT(!isRope());
+ if (isAtom()) {
+ return;
+ }
+ setFlagBit(DEPENDED_ON_BIT);
+ }
+
inline size_t allocSize() const;
/* Fallible conversions to more-derived string types. */
@@ -573,6 +617,11 @@ class JSString : public js::gc::CellWithLengthAndFlags {
bool isDependent() const { return flags() & DEPENDENT_BIT; }
MOZ_ALWAYS_INLINE
+ bool isAtomRef() const {
+ return (flags() & ATOM_REF_BIT) && !(flags() & ATOM_BIT);
+ }
+
+ MOZ_ALWAYS_INLINE
JSDependentString& asDependent() const {
MOZ_ASSERT(isDependent());
return *(JSDependentString*)this;
@@ -668,6 +717,8 @@ class JSString : public js::gc::CellWithLengthAndFlags {
inline JSLinearString* base() const;
+ inline JSAtom* atom() const;
+
// The base may be forwarded and becomes a relocation overlay.
// The return value can be a relocation overlay when the base is forwarded,
// or the return value can be the actual base when it is not forwarded.
@@ -678,6 +729,8 @@ class JSString : public js::gc::CellWithLengthAndFlags {
// Only called by the GC during nursery collection.
inline void setBase(JSLinearString* newBase);
+ bool tryReplaceWithAtomRef(JSAtom* atom);
+
void traceBase(JSTracer* trc);
/* Only called by the GC for strings with the AllocKind::STRING kind. */
@@ -1182,6 +1235,20 @@ class JSDependentString : public JSLinearString {
static_assert(sizeof(JSDependentString) == sizeof(JSString),
"string subclasses must be binary-compatible with JSString");
+class JSAtomRefString : public JSDependentString {
+ friend class JSString;
+ friend class js::gc::CellAllocator;
+ friend class js::jit::MacroAssembler;
+
+ public:
+ inline static size_t offsetOfAtom() {
+ return offsetof(JSAtomRefString, d.s.u3.atom);
+ }
+};
+
+static_assert(sizeof(JSAtomRefString) == sizeof(JSString),
+ "string subclasses must be binary-compatible with JSString");
+
class JSExtensibleString : public JSLinearString {
/* Vacuous and therefore unimplemented. */
bool isExtensible() const = delete;
@@ -1737,7 +1804,9 @@ inline JSLinearString* NewStringCopy(
}
/* Copy a counted string and GC-allocate a descriptor for it. */
-template <js::AllowGC allowGC, typename CharT>
+template <
+ js::AllowGC allowGC, typename CharT,
+ typename std::enable_if_t<!std::is_same_v<CharT, unsigned char>>* = nullptr>
inline JSLinearString* NewStringCopy(
JSContext* cx, std::basic_string_view<CharT> s,
js::gc::Heap heap = js::gc::Heap::Default) {
@@ -2030,10 +2099,19 @@ MOZ_ALWAYS_INLINE JSLinearString* JSString::ensureLinear(JSContext* cx) {
inline JSLinearString* JSString::base() const {
MOZ_ASSERT(hasBase());
- MOZ_ASSERT(!d.s.u3.base->isInline());
+ MOZ_ASSERT_IF(!isAtomRef(), !d.s.u3.base->isInline());
+ MOZ_ASSERT(d.s.u3.base->assertIsValidBase());
+ if (isAtomRef()) {
+ return static_cast<JSLinearString*>(d.s.u3.atom);
+ }
return d.s.u3.base;
}
+inline JSAtom* JSString::atom() const {
+ MOZ_ASSERT(isAtomRef());
+ return d.s.u3.atom;
+}
+
inline JSLinearString* JSString::nurseryBaseOrRelocOverlay() const {
MOZ_ASSERT(hasBase());
return d.s.u3.base;
@@ -2184,7 +2262,9 @@ MOZ_ALWAYS_INLINE bool JSAtom::lengthFitsInline<char16_t>(size_t length) {
template <>
MOZ_ALWAYS_INLINE void JSString::setNonInlineChars(const char16_t* chars) {
// Check that the new buffer is located in the StringBufferArena
- checkStringCharsArena(chars);
+ if (!(isAtomRef() && atom()->isInline())) {
+ checkStringCharsArena(chars);
+ }
d.s.u2.nonInlineCharsTwoByte = chars;
}
@@ -2192,7 +2272,9 @@ template <>
MOZ_ALWAYS_INLINE void JSString::setNonInlineChars(
const JS::Latin1Char* chars) {
// Check that the new buffer is located in the StringBufferArena
- checkStringCharsArena(chars);
+ if (!(isAtomRef() && atom()->isInline())) {
+ checkStringCharsArena(chars);
+ }
d.s.u2.nonInlineCharsLatin1 = chars;
}