/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#ifndef vm_StringType_h#define vm_StringType_h#include"mozilla/Maybe.h"#include"mozilla/MemoryReporting.h"#include"mozilla/Range.h"#include"mozilla/RefPtr.h"#include"mozilla/Span.h"#include"mozilla/StringBuffer.h"#include"mozilla/TextUtils.h"#include<string_view> // std::basic_string_view#include"jstypes.h" // js::Bit#include"gc/Cell.h"#include"gc/MaybeRooted.h"#include"gc/Nursery.h"#include"gc/RelocationOverlay.h"#include"gc/StoreBuffer.h"#include"js/CharacterEncoding.h"#include"js/RootingAPI.h"#include"js/shadow/String.h" // JS::shadow::String#include"js/String.h" // JS::MaxStringLength#include"js/UniquePtr.h"#include"util/Text.h"classJSDependentString;classJSExtensibleString;classJSExternalString;classJSInlineString;classJSRope;namespaceJS{classJS_PUBLIC_APIAutoStableStringChars;}// namespace JSnamespacejs{classArrayObject;classJS_PUBLIC_APIGenericPrinter;classJSONPrinter;classPropertyName;classStringBuilder;classJSOffThreadAtom;namespacefrontend{classParserAtomsTable;classTaggedParserAtomIndex;classWellKnownParserAtoms;structCompilationAtomCache;}// namespace frontendnamespacejit{classMacroAssembler;}// namespace jit/* The buffer length required to contain any unsigned 32-bit integer. */staticconstsize_tUINT32_CHAR_BUFFER_LENGTH=sizeof("4294967295")-1;// Maximum array index. This value is defined in the spec (ES2021 draft, 6.1.7)://// An array index is an integer index whose numeric value i is in the range// +0𝔽 ≤ i < 𝔽(2^32 - 1).constuint32_tMAX_ARRAY_INDEX=4294967294u;// 2^32-2 (= UINT32_MAX-1)// Returns true if the characters of `s` store an unsigned 32-bit integer value// less than or equal to MAX_ARRAY_INDEX, initializing `*indexp` to that value// if so. Leading '0' isn't allowed except 0 itself.template<typenameCharT>boolCheckStringIsIndex(constCharT*s,size_tlength,uint32_t*indexp);}/* namespace js */// clang-format off/* * [SMDOC] JavaScript Strings * * Conceptually, a JS string is just an array of chars and a length. This array * of chars may or may not be null-terminated and, if it is, the null character * is not included in the length. * * To improve performance of common operations, the following optimizations are * made which affect the engine's representation of strings: * * - The plain vanilla representation is a "linear" string which consists of a * string header in the GC heap and a malloc'd char array. * * - To avoid copying a substring of an existing "base" string , a "dependent" * string (JSDependentString) can be created which points into the base * string's char array. * * - To avoid O(n^2) char buffer copying, a "rope" node (JSRope) can be created * to represent a delayed string concatenation. Concatenation (called * flattening) is performed if and when a linear char array is requested. In * general, ropes form a binary dag whose internal nodes are JSRope string * headers with no associated char array and whose leaf nodes are linear * strings. * * - To avoid copying the leftmost string when flattening, we may produce an * "extensible" string, which tracks not only its actual length but also its * buffer's overall size. If such an "extensible" string appears as the * leftmost string in a subsequent flatten, and its buffer has enough unused * space, we can simply flatten the rest of the ropes into its buffer, * leaving its text in place. We then transfer ownership of its buffer to the * flattened rope, and mutate the donor extensible string into a dependent * string referencing its original buffer. * * (The term "extensible" does not imply that we ever 'realloc' the buffer. * Extensible strings may have dependent strings pointing into them, and the * JSAPI hands out pointers to linear strings' buffers, so resizing with * 'realloc' is generally not possible.) * * - To avoid allocating small char arrays, short strings can be stored inline * in the string header (JSInlineString). These come in two flavours: * JSThinInlineString, which is the same size as JSString; and * JSFatInlineString, which has a larger header and so can fit more chars. * * - To avoid comparing O(n) string equality comparison, strings can be * canonicalized to "atoms" (JSAtom) such that there is a single atom with a * given (length,chars). * * - To avoid copying all strings created through the JSAPI, an "external" * 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. * * - To avoid slow conversions from strings to integer indexes, we cache 16 bit * unsigned indexes on strings representing such numbers. * * 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) get(Latin1|TwoByte)CharsZ, get(Latin1|TwoByte)Chars, length / - * | \ * | JSRope leftChild, rightChild / - * | * JSLinearString latin1Chars, twoByteChars / - * | * +-- JSDependentString base / - * | | * | +-- JSAtomRefString - / base points to an atom * | * +-- JSExternalString - / char array memory managed by embedding * | * +-- JSExtensibleString - / tracks total buffer capacity (including current text) * | * +-- JSInlineString (abstract) - / chars stored in header * | | * | +-- JSThinInlineString - / header is normal * | | * | +-- JSFatInlineString - / header is fat * | * JSAtom (abstract) - / string equality === pointer equality * | | * | +-- js::NormalAtom JSLinearString + atom hash code / - * | | | * | | +-- js::ThinInlineAtom * | | possibly larger JSThinInlineString + atom hash code / - * | | * | +-- js::FatInlineAtom JSFatInlineString w/atom hash code / - * | * js::PropertyName - / chars don't contain an index (uint32_t) * * Classes marked with (abstract) above are not literally C++ Abstract Base * Classes (since there are no virtual functions, pure or not, in this * hierarchy), but have the same meaning: there are no strings with this type as * its most-derived type. * * Atoms can additionally be permanent, i.e. unable to be collected, and can * be combined with other string types to create additional most-derived types * that satisfy the invariants of more than one of the abovementioned * most-derived types. Furthermore, each atom stores a hash number (based on its * chars). This hash number is used as key in the atoms table and when the atom * is used as key in a JS Map/Set. * * Derived string types can be queried from ancestor types via isX() and * retrieved with asX() debug-only-checked casts. * * The ensureX() operations mutate 'this' in place to effectively make the type * be at least X (e.g., ensureLinear will change a JSRope to be a JSLinearString). */// clang-format onclassJSString:publicjs::gc::CellWithLengthAndFlags{protected:usingBase=js::gc::CellWithLengthAndFlags;staticconstsize_tNUM_INLINE_CHARS_LATIN1=2*sizeof(void*)/sizeof(JS::Latin1Char);staticconstsize_tNUM_INLINE_CHARS_TWO_BYTE=2*sizeof(void*)/sizeof(char16_t);public:// String length and flags are stored in the cell header.MOZ_ALWAYS_INLINEsize_tlength()const{returnheaderLengthField();}MOZ_ALWAYS_INLINEuint32_tflags()const{returnheaderFlagsField();}// Class for temporarily holding character data that will be used for JSString// contents. The data may be allocated in the nursery, the malloc heap, or as// a StringBuffer. The class instance must be passed to the JSString// constructor as a MutableHandle, so that if a GC occurs between the// construction of the content and the construction of the JSString Cell to// hold it, the contents can be transparently moved to the malloc heap before// the nursery is reset.template<typenameCharT>classOwnedChars{public:enumclassKind{// Not owning any chars. chars_ should not be used.Uninitialized,// chars_ is a buffer allocated in the nursery.Nursery,// chars_ is a buffer allocated in the malloc heap. This pointer should be// passed to js_free() if OwnedChars dies while still possessing// ownership.Malloc,// chars_ is allocated as a refcounted StringBuffer. The reference must be// released if OwnedChars dies while still possessing ownership.StringBuffer,};private:mozilla::Span<CharT>chars_;Kindkind_=Kind::Uninitialized;public:OwnedChars()=default;OwnedChars(CharT*chars,size_tlength,Kindkind);OwnedChars(js::UniquePtr<CharT[],JS::FreePolicy>&&chars,size_tlength);OwnedChars(RefPtr<mozilla::StringBuffer>&&buffer,size_tlength);OwnedChars(OwnedChars&&);OwnedChars(constOwnedChars&)=delete;~OwnedChars(){reset();}OwnedChars&operator=(OwnedChars&&);OwnedChars&operator=(constOwnedChars&)=delete;explicitoperatorbool()const{MOZ_ASSERT_IF(kind_!=Kind::Uninitialized,!chars_.empty());returnkind_!=Kind::Uninitialized;}mozilla::Span<CharT>span()const{MOZ_ASSERT(kind_!=Kind::Uninitialized);returnchars_;}CharT*data()const{MOZ_ASSERT(kind_!=Kind::Uninitialized);returnchars_.data();}size_tlength()const{MOZ_ASSERT(kind_!=Kind::Uninitialized);returnchars_.Length();}size_tsize()const{returnlength()*sizeof(CharT);}boolisMalloced()const{returnkind_==Kind::Malloc;}boolhasStringBuffer()const{returnkind_==Kind::StringBuffer;}// Return the data and release ownership to the caller.inlineCharT*release();// Discard any owned data.inlinevoidreset();// Move any nursery data into the malloc heap.inlinevoidensureNonNursery();// If we GC with a live OwnedChars, copy the data out of the nursery to a// safely malloced location.voidtrace(JSTracer*trc){ensureNonNursery();}};protected:/* Fields only apply to string types commented on the right. */structData{// Note: 32-bit length and flags fields are inherited from// CellWithLengthAndFlags.union{union{/* JS(Fat)InlineString */JS::Latin1CharinlineStorageLatin1[NUM_INLINE_CHARS_LATIN1];char16_tinlineStorageTwoByte[NUM_INLINE_CHARS_TWO_BYTE];};struct{union{constJS::Latin1Char*nonInlineCharsLatin1;/* JSLinearString, except JS(Fat)InlineString */constchar16_t*nonInlineCharsTwoByte;/* JSLinearString, except JS(Fat)InlineString */JSString*left;/* JSRope */JSRope*parent;/* Used in flattening */}u2;union{JSLinearString*base;/* JSDependentString */JSAtom*atom;/* JSAtomRefString */JSString*right;/* JSRope */size_tcapacity;/* JSLinearString (extensible) */constJSExternalStringCallbacks*externalCallbacks;/* JSExternalString */}u3;}s;};}d;public:/* Flags exposed only for jits *//* * Flag Encoding * * The first word of a JSString stores flags, index, and (on some * platforms) the length. The flags store 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. When we flatten a TwoByte rope, we turn child ropes * (including Latin1 ropes) into TwoByte dependent strings. If one of these * strings is also part of another Latin1 rope tree, we can have a Latin1 rope * with a TwoByte descendent. * * 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, 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 * string instance of that type. Abstract types have no instances and thus * have no such entry. The "subtype predicate" entry for a type specifies * the predicate used to query whether a JSString instance is subtype * (reflexively) of that type. * * String Instance Subtype * type encoding predicate * ----------------------------------------- * 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 * FORWARD_BIT for forwarded nursery cells. The other 2 bits are currently * unused. * * Note that the first 4 flag bits 3..6 (from right to left in the previous * table) have the following meaning and can be used for some hot queries: * * Bit 3: IsAtom (Atom, PermanentAtom) * Bit 4: IsLinear * Bit 5: IsDependent * Bit 6: IsInline (Inline, FatInline, ThinInlineAtom, FatInlineAtom) * * If INDEX_VALUE_BIT is set, bits 16 and up will also hold an integer index. */// The low bits of flag word are reserved by GC.static_assert(js::gc::CellFlagBitsReservedForGC<=3,"JSString::flags must reserve enough bits for Cell");staticconstuint32_tATOM_BIT=js::Bit(3);staticconstuint32_tLINEAR_BIT=js::Bit(4);staticconstuint32_tDEPENDENT_BIT=js::Bit(5);staticconstuint32_tINLINE_CHARS_BIT=js::Bit(6);// Indicates a dependent string pointing to an atomstaticconstuint32_tATOM_REF_BIT=js::Bit(9);staticconstuint32_tLINEAR_IS_EXTENSIBLE_BIT=js::Bit(7);staticconstuint32_tINLINE_IS_FAT_BIT=js::Bit(7);staticconstuint32_tLINEAR_IS_EXTERNAL_BIT=js::Bit(8);staticconstuint32_tATOM_IS_PERMANENT_BIT=js::Bit(8);staticconstuint32_tEXTENSIBLE_FLAGS=LINEAR_BIT|LINEAR_IS_EXTENSIBLE_BIT;staticconstuint32_tEXTERNAL_FLAGS=LINEAR_BIT|LINEAR_IS_EXTERNAL_BIT;staticconstuint32_tFAT_INLINE_MASK=INLINE_CHARS_BIT|INLINE_IS_FAT_BIT;/* Initial flags for various types of strings. */staticconstuint32_tINIT_THIN_INLINE_FLAGS=LINEAR_BIT|INLINE_CHARS_BIT;staticconstuint32_tINIT_FAT_INLINE_FLAGS=LINEAR_BIT|FAT_INLINE_MASK;staticconstuint32_tINIT_ROPE_FLAGS=0;staticconstuint32_tINIT_LINEAR_FLAGS=LINEAR_BIT;staticconstuint32_tINIT_DEPENDENT_FLAGS=LINEAR_BIT|DEPENDENT_BIT;staticconstuint32_tINIT_ATOM_REF_FLAGS=INIT_DEPENDENT_FLAGS|ATOM_REF_BIT;staticconstuint32_tTYPE_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.staticconstuint32_tATOM_IS_INDEX_BIT=js::Bit(9);// Linear strings:// - Content and representation are Latin-1 characters.// - Unmodifiable after construction.//// Ropes:// - Content are Latin-1 characters.// - Flag may be cleared when the rope is changed into a dependent string.//// Also see LATIN1_CHARS_BIT description under "Flag Encoding".staticconstuint32_tLATIN1_CHARS_BIT=js::Bit(10);staticconstuint32_tINDEX_VALUE_BIT=js::Bit(11);staticconstuint32_tINDEX_VALUE_SHIFT=16;// Whether this is a non-inline linear string with a refcounted// mozilla::StringBuffer.//// If set, d.s.u2.nonInlineChars* still points to the string's characters and// the StringBuffer header is stored immediately before the characters. This// allows recovering the StringBuffer from the chars pointer with// StringBuffer::FromData.staticconstuint32_tHAS_STRING_BUFFER_BIT=js::Bit(12);// NON_DEDUP_BIT is used in string deduplication during tenuring. This bit is// shared with both FLATTEN_FINISH_NODE and ATOM_IS_PERMANENT_BIT, since it// only applies to linear non-atoms.staticconstuint32_tNON_DEDUP_BIT=js::Bit(15);// If IN_STRING_TO_ATOM_CACHE is set, this string had an entry in the// StringToAtomCache at some point. Note that GC can purge the cache without// clearing this bit.staticconstuint32_tIN_STRING_TO_ATOM_CACHE=js::Bit(13);// Flags used during rope flattening that indicate what action to perform when// returning to the rope's parent rope.staticconstuint32_tFLATTEN_VISIT_RIGHT=js::Bit(14);staticconstuint32_tFLATTEN_FINISH_NODE=js::Bit(15);staticconstuint32_tFLATTEN_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.staticconstuint32_tDEPENDED_ON_BIT=FLATTEN_VISIT_RIGHT;staticconstuint32_tPINNED_ATOM_BIT=js::Bit(15);staticconstuint32_tPERMANENT_ATOM_MASK=ATOM_BIT|PINNED_ATOM_BIT|ATOM_IS_PERMANENT_BIT;staticconstuint32_tMAX_LENGTH=JS::MaxStringLength;staticconstJS::Latin1CharMAX_LATIN1_CHAR=0xff;// Allocate a StringBuffer instead of using raw malloc for strings with// length * sizeof(CharT) >= MIN_BYTES_FOR_BUFFER.//// StringBuffers can be shared more efficiently with DOM code, but have some// additional overhead (StringBuffer header, null terminator) so for short// strings we prefer malloc.//// Note that 514 was picked as a pretty conservative initial value. The value// is just above 512 to ensure a Latin1 string of length 512 isn't bumped// from jemalloc bucket size 512 to size 768. It's an even value because it's// divided by 2 for char16_t strings.staticconstexprsize_tMIN_BYTES_FOR_BUFFER=514;/* * Helper function to validate that a string of a given length is * representable by a JSString. An allocation overflow is reported if false * is returned. */staticinlineboolvalidateLength(JSContext*cx,size_tlength);template<js::AllowGCallowGC>staticinlineboolvalidateLengthInternal(JSContext*cx,size_tlength);staticconstexprsize_toffsetOfFlags(){returnoffsetOfHeaderFlags();}staticconstexprsize_toffsetOfLength(){returnoffsetOfHeaderLength();}boolsameLengthAndFlags(constJSString&other)const{returnlength()==other.length()&&flags()==other.flags();}staticvoidstaticAsserts(){static_assert(JSString::MAX_LENGTH<UINT32_MAX,"Length must fit in 32 bits");static_assert(sizeof(JSString)==(offsetof(JSString,d.inlineStorageLatin1)+NUM_INLINE_CHARS_LATIN1*sizeof(char)),"Inline Latin1 chars must fit in a JSString");static_assert(sizeof(JSString)==(offsetof(JSString,d.inlineStorageTwoByte)+NUM_INLINE_CHARS_TWO_BYTE*sizeof(char16_t)),"Inline char16_t chars must fit in a JSString");/* Ensure js::shadow::String has the same layout. */usingJS::shadow::String;static_assert(JSString::offsetOfRawHeaderFlagsField()==offsetof(String,flags_),"shadow::String flags offset must match JSString");#if JS_BITS_PER_WORD == 32static_assert(JSString::offsetOfLength()==offsetof(String,length_),"shadow::String length offset must match JSString");#endifstatic_assert(offsetof(JSString,d.s.u2.nonInlineCharsLatin1)==offsetof(String,nonInlineCharsLatin1),"shadow::String nonInlineChars offset must match JSString");static_assert(offsetof(JSString,d.s.u2.nonInlineCharsTwoByte)==offsetof(String,nonInlineCharsTwoByte),"shadow::String nonInlineChars offset must match JSString");static_assert(offsetof(JSString,d.s.u3.externalCallbacks)==offsetof(String,externalCallbacks),"shadow::String externalCallbacks offset must match JSString");static_assert(offsetof(JSString,d.inlineStorageLatin1)==offsetof(String,inlineStorageLatin1),"shadow::String inlineStorage offset must match JSString");static_assert(offsetof(JSString,d.inlineStorageTwoByte)==offsetof(String,inlineStorageTwoByte),"shadow::String inlineStorage offset must match JSString");static_assert(ATOM_BIT==String::ATOM_BIT,"shadow::String::ATOM_BIT must match JSString::ATOM_BIT");static_assert(LINEAR_BIT==String::LINEAR_BIT,"shadow::String::LINEAR_BIT must match JSString::LINEAR_BIT");static_assert(INLINE_CHARS_BIT==String::INLINE_CHARS_BIT,"shadow::String::INLINE_CHARS_BIT must match ""JSString::INLINE_CHARS_BIT");static_assert(LATIN1_CHARS_BIT==String::LATIN1_CHARS_BIT,"shadow::String::LATIN1_CHARS_BIT must match ""JSString::LATIN1_CHARS_BIT");static_assert(TYPE_FLAGS_MASK==String::TYPE_FLAGS_MASK,"shadow::String::TYPE_FLAGS_MASK must match JSString::TYPE_FLAGS_MASK");static_assert(EXTERNAL_FLAGS==String::EXTERNAL_FLAGS,"shadow::String::EXTERNAL_FLAGS must match JSString::EXTERNAL_FLAGS");}/* Avoid silly compile errors in JSRope::flatten */friendclassJSRope;friendclassjs::gc::RelocationOverlay;protected:template<typenameCharT>MOZ_ALWAYS_INLINEvoidsetNonInlineChars(constCharT*chars,boolusesStringBuffer);template<typenameCharT>staticMOZ_ALWAYS_INLINEvoidcheckStringCharsArena(constCharT*chars,boolusesStringBuffer){#ifdef MOZ_DEBUG// Check that the new buffer is located in the StringBufferArena.// For now ignore this for StringBuffers because they can be allocated in// the main jemalloc arena.if(!usesStringBuffer){js::AssertJSStringBufferInCorrectArena(chars);}#endif}// Get correct non-inline chars enum arm for given typetemplate<typenameCharT>MOZ_ALWAYS_INLINEconstCharT*nonInlineCharsRaw()const;public:MOZ_ALWAYS_INLINEboolempty()const{returnlength()==0;}inlineboolgetChar(JSContext*cx,size_tindex,char16_t*code);inlineboolgetCodePoint(JSContext*cx,size_tindex,char32_t*codePoint);/* Strings have either Latin1 or TwoByte chars. */boolhasLatin1Chars()const{returnflags()&LATIN1_CHARS_BIT;}boolhasTwoByteChars()const{return!(flags()&LATIN1_CHARS_BIT);}/* Strings might contain cached indexes. */boolhasIndexValue()const{returnflags()&INDEX_VALUE_BIT;}uint32_tgetIndexValue()const{MOZ_ASSERT(hasIndexValue());MOZ_ASSERT(isLinear());returnflags()>>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. */boolisDependedOn()const{boolresult=flags()&DEPENDED_ON_BIT;MOZ_ASSERT_IF(result,!isRope()&&!isAtom());returnresult;}boolassertIsValidBase()const{// See isDependedOn comment for why we're excluding atomsreturnisAtom()||isDependedOn();}voidsetDependedOn(){MOZ_ASSERT(!isRope());if(isAtom()){return;}setFlagBit(DEPENDED_ON_BIT);}inlinesize_tallocSize()const;/* Fallible conversions to more-derived string types. */inlineJSLinearString*ensureLinear(JSContext*cx);/* Type query and debug-checked casts */MOZ_ALWAYS_INLINEboolisRope()const{return!(flags()&LINEAR_BIT);}MOZ_ALWAYS_INLINEJSRope&asRope()const{MOZ_ASSERT(isRope());return*(JSRope*)this;}MOZ_ALWAYS_INLINEboolisLinear()const{returnflags()&LINEAR_BIT;}MOZ_ALWAYS_INLINEJSLinearString&asLinear()const{MOZ_ASSERT(JSString::isLinear());return*(JSLinearString*)this;}MOZ_ALWAYS_INLINEboolisDependent()const{returnflags()&DEPENDENT_BIT;}MOZ_ALWAYS_INLINEboolisAtomRef()const{return(flags()&ATOM_REF_BIT)&&!(flags()&ATOM_BIT);}MOZ_ALWAYS_INLINEJSDependentString&asDependent()const{MOZ_ASSERT(isDependent());return*(JSDependentString*)this;}MOZ_ALWAYS_INLINEboolisExtensible()const{return(flags()&TYPE_FLAGS_MASK)==EXTENSIBLE_FLAGS;}MOZ_ALWAYS_INLINEJSExtensibleString&asExtensible()const{MOZ_ASSERT(isExtensible());return*(JSExtensibleString*)this;}MOZ_ALWAYS_INLINEboolisInline()const{returnflags()&INLINE_CHARS_BIT;}MOZ_ALWAYS_INLINEJSInlineString&asInline()const{MOZ_ASSERT(isInline());return*(JSInlineString*)this;}MOZ_ALWAYS_INLINEboolisFatInline()const{return(flags()&FAT_INLINE_MASK)==FAT_INLINE_MASK;}/* For hot code, prefer other type queries. */boolisExternal()const{return(flags()&TYPE_FLAGS_MASK)==EXTERNAL_FLAGS;}MOZ_ALWAYS_INLINEJSExternalString&asExternal()const{MOZ_ASSERT(isExternal());return*(JSExternalString*)this;}MOZ_ALWAYS_INLINEboolisAtom()const{returnflags()&ATOM_BIT;}MOZ_ALWAYS_INLINEboolisPermanentAtom()const{return(flags()&PERMANENT_ATOM_MASK)==PERMANENT_ATOM_MASK;}MOZ_ALWAYS_INLINEJSAtom&asAtom()const{MOZ_ASSERT(isAtom());return*(JSAtom*)this;}MOZ_ALWAYS_INLINEjs::JSOffThreadAtom&asOffThreadAtom()const{MOZ_ASSERT(headerFlagsFieldAtomic()&ATOM_BIT);return*(js::JSOffThreadAtom*)this;}MOZ_ALWAYS_INLINEvoidsetNonDeduplicatable(){MOZ_ASSERT(isLinear());MOZ_ASSERT(!isAtom());setFlagBit(NON_DEDUP_BIT);}// After copying a string from the nursery to the tenured heap, adjust bits// that no longer apply.MOZ_ALWAYS_INLINEvoidclearBitsOnTenure(){MOZ_ASSERT(!isAtom());clearFlagBit(NON_DEDUP_BIT|IN_STRING_TO_ATOM_CACHE);}// NON_DEDUP_BIT is only valid for linear non-atoms.MOZ_ALWAYS_INLINEboolisDeduplicatable()const{MOZ_ASSERT(isLinear());MOZ_ASSERT(!isAtom());return!(flags()&NON_DEDUP_BIT);}voidsetInStringToAtomCache(){MOZ_ASSERT(!isAtom());setFlagBit(IN_STRING_TO_ATOM_CACHE);}boolinStringToAtomCache()const{returnflags()&IN_STRING_TO_ATOM_CACHE;}// Fills |array| with various strings that represent the different string// kinds and character encodings.staticboolfillWithRepresentatives(JSContext*cx,JS::Handle<js::ArrayObject*>array);/* Only called by the GC for dependent strings. */inlineboolhasBase()const{returnisDependent();}inlineJSLinearString*base()const;inlineJSAtom*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.inlineJSLinearString*nurseryBaseOrRelocOverlay()const;inlineboolcanOwnDependentChars()const;booltryReplaceWithAtomRef(JSAtom*atom);voidtraceBase(JSTracer*trc);/* Only called by the GC for strings with the AllocKind::STRING kind. */inlinevoidfinalize(JS::GCContext*gcx);/* Gets the number of bytes that the chars take on the heap. */size_tsizeOfExcludingThis(mozilla::MallocSizeOfmallocSizeOf);boolhasOutOfLineChars()const{returnisLinear()&&!isInline()&&!isDependent()&&!isExternal();}inlineboolownsMallocedChars()const;boolhasStringBuffer()const{MOZ_ASSERT_IF(flags()&HAS_STRING_BUFFER_BIT,isLinear()&&!isInline()&&!isDependent()&&!isExternal());returnflags()&HAS_STRING_BUFFER_BIT;}/* Encode as many scalar values of the string as UTF-8 as can fit * into the caller-provided buffer replacing unpaired surrogates * with the REPLACEMENT CHARACTER. * * Returns the number of code units read and the number of code units * written. * * The semantics of this method match the semantics of * TextEncoder.encodeInto(). * * This function doesn't modify the representation -- rope, linear, * flat, atom, etc. -- of this string. If this string is a rope, * it also doesn't modify the representation of left or right halves * of this string, or of those halves, and so on. * * Returns mozilla::Nothing on OOM. */mozilla::Maybe<std::tuple<size_t,size_t>>encodeUTF8Partial(constJS::AutoRequireNoGC&nogc,mozilla::Span<char>buffer)const;private:// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler// to call the method below.friendclassjs::jit::MacroAssembler;staticsize_toffsetOfNonInlineChars(){static_assert(offsetof(JSString,d.s.u2.nonInlineCharsTwoByte)==offsetof(JSString,d.s.u2.nonInlineCharsLatin1),"nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");returnoffsetof(JSString,d.s.u2.nonInlineCharsTwoByte);}public:staticconstJS::TraceKindTraceKind=JS::TraceKind::String;JS::Zone*zone()const{if(isTenured()){// Allow permanent atoms to be accessed across zones and runtimes.if(isPermanentAtom()){returnzoneFromAnyThread();}returnasTenured().zone();}returnnurseryZone();}voidsetLengthAndFlags(uint32_tlen,uint32_tflags){setHeaderLengthAndFlags(len,flags);}voidsetFlagBit(uint32_tflag){setHeaderFlagBit(flag);}voidclearFlagBit(uint32_tflag){clearHeaderFlagBit(flag);}voidfixupAfterMovingGC(){}js::gc::AllocKindgetAllocKind()const{usingjs::gc::AllocKind;AllocKindkind;if(isAtom()){if(isFatInline()){kind=AllocKind::FAT_INLINE_ATOM;}else{kind=AllocKind::ATOM;}}elseif(isFatInline()){kind=AllocKind::FAT_INLINE_STRING;}elseif(isExternal()){kind=AllocKind::EXTERNAL_STRING;}else{kind=AllocKind::STRING;}MOZ_ASSERT_IF(isTenured(),kind==asTenured().getAllocKind());returnkind;}#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddump()const;voiddump(js::GenericPrinter&out)const;voiddump(js::JSONPrinter&json)const;voiddumpCommonFields(js::JSONPrinter&json)const;voiddumpCharsFields(js::JSONPrinter&json)const;voiddumpFields(js::JSONPrinter&json)const;voiddumpStringContent(js::GenericPrinter&out)const;voiddumpPropertyName(js::GenericPrinter&out)const;voiddumpChars(js::GenericPrinter&out)const;voiddumpCharsSingleQuote(js::GenericPrinter&out)const;voiddumpCharsNoQuote(js::GenericPrinter&out)const;template<typenameCharT>staticvoiddumpCharsNoQuote(constCharT*s,size_tlen,js::GenericPrinter&out);voiddumpRepresentation()const;voiddumpRepresentation(js::GenericPrinter&out)const;voiddumpRepresentation(js::JSONPrinter&json)const;voiddumpRepresentationFields(js::JSONPrinter&json)const;boolequals(constchar*s);#endifvoidtraceChildren(JSTracer*trc);// Override base class implementation to tell GC about permanent atoms.boolisPermanentAndMayBeShared()const{returnisPermanentAtom();}staticvoidaddCellAddressToStoreBuffer(js::gc::StoreBuffer*buffer,js::gc::Cell**cellp){buffer->putCell(reinterpret_cast<JSString**>(cellp));}staticvoidremoveCellAddressFromStoreBuffer(js::gc::StoreBuffer*buffer,js::gc::Cell**cellp){buffer->unputCell(reinterpret_cast<JSString**>(cellp));}private:JSString(constJSString&other)=delete;voidoperator=(constJSString&other)=delete;protected:JSString()=default;};namespacejs{template<typenameWrapper,typenameCharT>classWrappedPtrOperations<JSString::OwnedChars<CharT>,Wrapper>{constJSString::OwnedChars<CharT>&get()const{returnstatic_cast<constWrapper*>(this)->get();}public:explicitoperatorbool()const{return!!get();}mozilla::Span<CharT>span()const{returnget().span();}CharT*data()const{returnget().data();}size_tlength()const{returnget().length();}size_tsize()const{returnget().size();}boolisMalloced()const{returnget().isMalloced();}boolhasStringBuffer()const{returnget().hasStringBuffer();}};template<typenameWrapper,typenameCharT>classMutableWrappedPtrOperations<JSString::OwnedChars<CharT>,Wrapper>:publicWrappedPtrOperations<JSString::OwnedChars<CharT>,Wrapper>{JSString::OwnedChars<CharT>&get(){returnstatic_cast<Wrapper*>(this)->get();}public:CharT*release(){returnget().release();}voidreset(){get().reset();}voidensureNonNursery(){get().ensureNonNursery();}};}/* namespace js */classJSRope:publicJSString{friendclassjs::gc::CellAllocator;template<typenameCharT>js::UniquePtr<CharT[],JS::FreePolicy>copyCharsInternal(JSContext*cx,arena_id_tdestArenaId)const;enumUsingBarrier:bool{NoBarrier=false,WithIncrementalBarrier=true};friendclassJSString;JSLinearString*flatten(JSContext*maybecx);JSLinearString*flattenInternal();template<UsingBarrierusingBarrier>JSLinearString*flattenInternal();template<UsingBarrierusingBarrier,typenameCharT>staticJSLinearString*flattenInternal(JSRope*root);template<UsingBarrierusingBarrier>staticvoidropeBarrierDuringFlattening(JSRope*rope);JSRope(JSString*left,JSString*right,size_tlength);public:template<js::AllowGCallowGC>staticinlineJSRope*new_(JSContext*cx,typenamejs::MaybeRooted<JSString*,allowGC>::HandleTypeleft,typenamejs::MaybeRooted<JSString*,allowGC>::HandleTyperight,size_tlength,js::gc::Heap=js::gc::Heap::Default);js::UniquePtr<JS::Latin1Char[],JS::FreePolicy>copyLatin1Chars(JSContext*maybecx,arena_id_tdestArenaId)const;JS::UniqueTwoByteCharscopyTwoByteChars(JSContext*maybecx,arena_id_tdestArenaId)const;template<typenameCharT>js::UniquePtr<CharT[],JS::FreePolicy>copyChars(JSContext*maybecx,arena_id_tdestArenaId)const;// Hash function specific for ropes that avoids allocating a temporary// string. There are still allocations internally so it's technically// fallible.//// Returns the same value as if this were a linear string being hashed.[[nodiscard]]boolhash(uint32_t*outhHash)const;// The process of flattening a rope temporarily overwrites the left pointer of// interior nodes in the rope DAG with the parent pointer.boolisBeingFlattened()const{returnflags()&FLATTEN_MASK;}JSString*leftChild()const{MOZ_ASSERT(isRope());MOZ_ASSERT(!isBeingFlattened());// Flattening overwrites this field.returnd.s.u2.left;}JSString*rightChild()const{MOZ_ASSERT(isRope());returnd.s.u3.right;}voidtraceChildren(JSTracer*trc);#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddumpOwnRepresentationFields(js::JSONPrinter&json)const;#endifprivate:// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler// to call the methods below.friendclassjs::jit::MacroAssembler;staticsize_toffsetOfLeft(){returnoffsetof(JSRope,d.s.u2.left);}staticsize_toffsetOfRight(){returnoffsetof(JSRope,d.s.u3.right);}};static_assert(sizeof(JSRope)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");/* * There are optimized entry points for some string allocation functions. * * The meaning of suffix: * * "MaybeDeflate": for char16_t variant, characters can fit Latin1 * * "DontDeflate": for char16_t variant, characters don't fit Latin1 * * "NonStatic": characters don't match StaticStrings * * "ValidLength": length fits JSString::MAX_LENGTH */classJSLinearString:publicJSString{friendclassJSString;friendclassJS::AutoStableStringChars;friendclassjs::gc::TenuringTracer;friendclassjs::gc::CellAllocator;friendclassJSDependentString;// To allow access when used as base./* Vacuous and therefore unimplemented. */JSLinearString*ensureLinear(JSContext*cx)=delete;boolisLinear()const=delete;JSLinearString&asLinear()const=delete;JSLinearString(constchar16_t*chars,size_tlength,boolhasBuffer);JSLinearString(constJS::Latin1Char*chars,size_tlength,boolhasBuffer);template<typenameCharT>explicitinlineJSLinearString(JS::MutableHandle<OwnedChars<CharT>>chars);protected:// Used to construct subclasses that do a full initialization themselves.JSLinearString()=default;/* Returns void pointer to latin1/twoByte chars, for finalizers. */MOZ_ALWAYS_INLINEvoid*nonInlineCharsRaw()const{MOZ_ASSERT(!isInline());static_assert(offsetof(JSLinearString,d.s.u2.nonInlineCharsTwoByte)==offsetof(JSLinearString,d.s.u2.nonInlineCharsLatin1),"nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");return(void*)d.s.u2.nonInlineCharsTwoByte;}MOZ_ALWAYS_INLINEconstJS::Latin1Char*rawLatin1Chars()const;MOZ_ALWAYS_INLINEconstchar16_t*rawTwoByteChars()const;public:template<js::AllowGCallowGC,typenameCharT>staticinlineJSLinearString*new_(JSContext*cx,JS::MutableHandle<OwnedChars<CharT>>chars,js::gc::Heapheap);template<js::AllowGCallowGC,typenameCharT>staticinlineJSLinearString*newValidLength(JSContext*cx,JS::MutableHandle<OwnedChars<CharT>>chars,js::gc::Heapheap);// Convert a plain linear string to an extensible string. For testing. The// caller must ensure that it is a plain or extensible string already, and// that `capacity` is adequate.JSExtensibleString&makeExtensible(size_tcapacity);template<typenameCharT>MOZ_ALWAYS_INLINEconstCharT*nonInlineChars(constJS::AutoRequireNoGC&nogc)const;MOZ_ALWAYS_INLINEconstJS::Latin1Char*nonInlineLatin1Chars(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(!isInline());MOZ_ASSERT(hasLatin1Chars());returnd.s.u2.nonInlineCharsLatin1;}MOZ_ALWAYS_INLINEconstchar16_t*nonInlineTwoByteChars(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(!isInline());MOZ_ASSERT(hasTwoByteChars());returnd.s.u2.nonInlineCharsTwoByte;}template<typenameCharT>MOZ_ALWAYS_INLINEconstCharT*chars(constJS::AutoRequireNoGC&nogc)const;MOZ_ALWAYS_INLINEconstJS::Latin1Char*latin1Chars(constJS::AutoRequireNoGC&nogc)const{returnrawLatin1Chars();}MOZ_ALWAYS_INLINEconstchar16_t*twoByteChars(constJS::AutoRequireNoGC&nogc)const{returnrawTwoByteChars();}mozilla::Range<constJS::Latin1Char>latin1Range(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(JSString::isLinear());returnmozilla::Range<constJS::Latin1Char>(latin1Chars(nogc),length());}mozilla::Range<constchar16_t>twoByteRange(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(JSString::isLinear());returnmozilla::Range<constchar16_t>(twoByteChars(nogc),length());}template<typenameCharT>mozilla::Range<constCharT>range(constJS::AutoRequireNoGC&nogc)const{ifconstexpr(std::is_same_v<CharT,JS::Latin1Char>){returnlatin1Range(nogc);}else{returntwoByteRange(nogc);}}MOZ_ALWAYS_INLINEchar16_tlatin1OrTwoByteChar(size_tindex)const{MOZ_ASSERT(JSString::isLinear());MOZ_ASSERT(index<length());JS::AutoCheckCannotGCnogc;returnhasLatin1Chars()?latin1Chars(nogc)[index]:twoByteChars(nogc)[index];}boolisIndexSlow(uint32_t*indexp)const{MOZ_ASSERT(JSString::isLinear());size_tlen=length();if(len==0||len>js::UINT32_CHAR_BUFFER_LENGTH){returnfalse;}JS::AutoCheckCannotGCnogc;if(hasLatin1Chars()){constJS::Latin1Char*s=latin1Chars(nogc);returnmozilla::IsAsciiDigit(*s)&&js::CheckStringIsIndex(s,len,indexp);}constchar16_t*s=twoByteChars(nogc);returnmozilla::IsAsciiDigit(*s)&&js::CheckStringIsIndex(s,len,indexp);}// Returns true if this string's characters store an unsigned 32-bit integer// value less than or equal to MAX_ARRAY_INDEX, initializing *indexp to that// value if so. Leading '0' isn't allowed except 0 itself.// (Thus if calling isIndex returns true, js::IndexToString(cx, *indexp) will// be a string equal to this string.)inlineboolisIndex(uint32_t*indexp)const;// Return whether the characters of this string can be moved by minor or// compacting GC.inlineboolhasMovableChars()const;boolhasCharsInCollectedNurseryRegion()const;voidmaybeInitializeIndexValue(uint32_tindex,boolallowAtom=false){MOZ_ASSERT(JSString::isLinear());MOZ_ASSERT_IF(hasIndexValue(),getIndexValue()==index);MOZ_ASSERT_IF(!allowAtom,!isAtom());if(hasIndexValue()||index>UINT16_MAX){return;}mozilla::DebugOnly<uint32_t>containedIndex;MOZ_ASSERT(isIndexSlow(&containedIndex));MOZ_ASSERT(index==containedIndex);setFlagBit((index<<INDEX_VALUE_SHIFT)|INDEX_VALUE_BIT);MOZ_ASSERT(getIndexValue()==index);}mozilla::StringBuffer*stringBuffer()const{MOZ_ASSERT(hasStringBuffer());auto*chars=nonInlineCharsRaw();returnmozilla::StringBuffer::FromData(const_cast<void*>(chars));}/* * Returns a property name represented by this string, or null on failure. * You must verify that this is not an index per isIndex before calling * this method. */inlinejs::PropertyName*toPropertyName(JSContext*cx);// Make sure chars are not in the nursery, mallocing and copying if necessary.// Should only be called during minor GC on a string that has been promoted// to the tenured heap and may still point to nursery-allocated chars.template<typenameCharT>inlinesize_tmaybeMallocCharsOnPromotion(js::Nursery*nursery);// Handle an edge case where a dependent chain N1 -> T2 -> N3 cannot handle N3// moving its chars (or more specifically, updating N1 to the new chars.) When// this is detected, convert N1 to a regular string with its own storage.//// Returns whether the chars were cloned.template<typenameCharT>staticvoidmaybeCloneCharsOnPromotionTyped(JSLinearString*str);staticvoidmaybeCloneCharsOnPromotion(JSLinearString*str){if(str->hasLatin1Chars()){maybeCloneCharsOnPromotionTyped<JS::Latin1Char>(str);}else{maybeCloneCharsOnPromotionTyped<char16_t>(str);}}inlinevoidfinalize(JS::GCContext*gcx);inlinesize_tallocSize()const;#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddumpOwnRepresentationFields(js::JSONPrinter&json)const;#endif// Make a partially-initialized string safe for finalization.inlinevoiddisownCharsBecauseError();};static_assert(sizeof(JSLinearString)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");namespaceJS{enumclassContractBaseChain:bool{AllowLong=false,Contract=true};}classJSDependentString:publicJSLinearString{friendclassJSString;friendclassjs::gc::CellAllocator;JSDependentString(JSLinearString*base,size_tstart,size_tlength);// For JIT string allocation.JSDependentString()=default;/* Vacuous and therefore unimplemented. */boolisDependent()const=delete;JSDependentString&asDependent()const=delete;/* The offset of this string's chars in base->chars(). */MOZ_ALWAYS_INLINEsize_tbaseOffset()const{MOZ_ASSERT(JSString::isDependent());JS::AutoCheckCannotGCnogc;size_toffset;if(hasTwoByteChars()){offset=twoByteChars(nogc)-base()->twoByteChars(nogc);}else{offset=latin1Chars(nogc)-base()->latin1Chars(nogc);}MOZ_ASSERT(offset<base()->length());returnoffset;}public:template<JS::ContractBaseChaincontract>staticinlineJSLinearString*newImpl_(JSContext*cx,JSLinearString*base,size_tstart,size_tlength,js::gc::Heapheap);// This will always return a dependent string, and will assert if the chars// could fit into an inline string.staticinlineJSLinearString*new_(JSContext*cx,JSLinearString*base,size_tstart,size_tlength,js::gc::Heapheap);// Only called by the GC during nursery collection.voidsetBase(JSLinearString*newBase);template<typenameT>voidrelocateBaseAndChars(JSLinearString*base,Tchars,size_toffset){MOZ_ASSERT(base->assertIsValidBase());boolusesStringBuffer=base->hasStringBuffer();setNonInlineChars(chars+offset,usesStringBuffer);setBase(base);}JSLinearString*rootBaseDuringMinorGC();template<typenameCharT>inlinevoidupdateToPromotedBaseImpl(JSLinearString*base);inlinevoidupdateToPromotedBase(JSLinearString*base);// Avoid creating a dependent string if no more than 6.25% (1/16) of the base// string are used, to prevent tiny dependent strings keeping large base// strings alive. (The percentage was chosen as a somewhat arbitrary threshold// that is easy to compute.)//// Note that currently this limit only applies during tenuring; in the// nursery, small dependent strings will be created but then cloned into// unshared strings during tenuring. (The base string will not be marked in// this case.)staticboolsmallComparedToBase(size_tsharedChars,size_tbaseChars){returnsharedChars<=(baseChars>>4);}#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddumpOwnRepresentationFields(js::JSONPrinter&json)const;#endifprivate:// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler// to call the method below.friendclassjs::jit::MacroAssembler;inlinestaticsize_toffsetOfBase(){returnoffsetof(JSDependentString,d.s.u3.base);}};static_assert(sizeof(JSDependentString)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");classJSAtomRefString:publicJSDependentString{friendclassJSString;friendclassjs::gc::CellAllocator;friendclassjs::jit::MacroAssembler;public:inlinestaticsize_toffsetOfAtom(){returnoffsetof(JSAtomRefString,d.s.u3.atom);}};static_assert(sizeof(JSAtomRefString)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");classJSExtensibleString:publicJSLinearString{/* Vacuous and therefore unimplemented. */boolisExtensible()const=delete;JSExtensibleString&asExtensible()const=delete;public:MOZ_ALWAYS_INLINEsize_tcapacity()const{MOZ_ASSERT(JSString::isExtensible());returnd.s.u3.capacity;}#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddumpOwnRepresentationFields(js::JSONPrinter&json)const;#endif};static_assert(sizeof(JSExtensibleString)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");classJSInlineString:publicJSLinearString{public:MOZ_ALWAYS_INLINEconstJS::Latin1Char*latin1Chars(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(JSString::isInline());MOZ_ASSERT(hasLatin1Chars());returnd.inlineStorageLatin1;}MOZ_ALWAYS_INLINEconstchar16_t*twoByteChars(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(JSString::isInline());MOZ_ASSERT(hasTwoByteChars());returnd.inlineStorageTwoByte;}template<typenameCharT>staticboollengthFits(size_tlength);#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddumpOwnRepresentationFields(js::JSONPrinter&json)const;#endifprivate:// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler// to call the method below.friendclassjs::jit::MacroAssembler;staticsize_toffsetOfInlineStorage(){returnoffsetof(JSInlineString,d.inlineStorageTwoByte);}};static_assert(sizeof(JSInlineString)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");/* * On 32-bit platforms, JSThinInlineString can store 8 Latin1 characters or 4 * TwoByte characters inline. On 64-bit platforms, these numbers are 16 and 8, * respectively. */classJSThinInlineString:publicJSInlineString{friendclassjs::gc::CellAllocator;// The constructors return a mutable pointer to the data, because the first// thing any creator will do is copy in the string value. This also// conveniently allows doing overload resolution on CharT.explicitJSThinInlineString(size_tlength,JS::Latin1Char**chars);explicitJSThinInlineString(size_tlength,char16_t**chars);// For JIT string allocation.JSThinInlineString()=default;public:staticconstexprsize_tInlineBytes=NUM_INLINE_CHARS_LATIN1;staticconstsize_tMAX_LENGTH_LATIN1=NUM_INLINE_CHARS_LATIN1;staticconstsize_tMAX_LENGTH_TWO_BYTE=NUM_INLINE_CHARS_TWO_BYTE;template<js::AllowGCallowGC>staticinlineJSThinInlineString*new_(JSContext*cx,js::gc::Heapheap);template<typenameCharT>staticboollengthFits(size_tlength);};static_assert(sizeof(JSThinInlineString)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");/* * On both 32-bit and 64-bit platforms, MAX_LENGTH_TWO_BYTE is 12 and * MAX_LENGTH_LATIN1 is 24. 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. TwoByte strings of length 5--8 will be JSFatInlineStrings on 32-bit * platforms and JSThinInlineStrings on 64-bit platforms. But the more * significant transition from inline strings to non-inline strings occurs at * length 12 (for TwoByte strings) and 24 (Latin1 strings) on both 32-bit and * 64-bit platforms. */classJSFatInlineString:publicJSInlineString{friendclassjs::gc::CellAllocator;staticconstsize_tINLINE_EXTENSION_CHARS_LATIN1=24-NUM_INLINE_CHARS_LATIN1;staticconstsize_tINLINE_EXTENSION_CHARS_TWO_BYTE=12-NUM_INLINE_CHARS_TWO_BYTE;// The constructors return a mutable pointer to the data, because the first// thing any creator will do is copy in the string value. This also// conveniently allows doing overload resolution on CharT.explicitJSFatInlineString(size_tlength,JS::Latin1Char**chars);explicitJSFatInlineString(size_tlength,char16_t**chars);// For JIT string allocation.JSFatInlineString()=default;protected:/* to fool clang into not warning this is unused */union{charinlineStorageExtensionLatin1[INLINE_EXTENSION_CHARS_LATIN1];char16_tinlineStorageExtensionTwoByte[INLINE_EXTENSION_CHARS_TWO_BYTE];};public:template<js::AllowGCallowGC>staticinlineJSFatInlineString*new_(JSContext*cx,js::gc::Heapheap);staticconstsize_tMAX_LENGTH_LATIN1=JSString::NUM_INLINE_CHARS_LATIN1+INLINE_EXTENSION_CHARS_LATIN1;staticconstsize_tMAX_LENGTH_TWO_BYTE=JSString::NUM_INLINE_CHARS_TWO_BYTE+INLINE_EXTENSION_CHARS_TWO_BYTE;template<typenameCharT>staticboollengthFits(size_tlength);// Only called by the GC for strings with the AllocKind::FAT_INLINE_STRING// kind.MOZ_ALWAYS_INLINEvoidfinalize(JS::GCContext*gcx);};static_assert(sizeof(JSFatInlineString)%js::gc::CellAlignBytes==0,"fat inline strings shouldn't waste space up to the next cell ""boundary");classJSExternalString:publicJSLinearString{friendclassjs::gc::CellAllocator;JSExternalString(constJS::Latin1Char*chars,size_tlength,constJSExternalStringCallbacks*callbacks);JSExternalString(constchar16_t*chars,size_tlength,constJSExternalStringCallbacks*callbacks);/* Vacuous and therefore unimplemented. */boolisExternal()const=delete;JSExternalString&asExternal()const=delete;template<typenameCharT>staticinlineJSExternalString*newImpl(JSContext*cx,constCharT*chars,size_tlength,constJSExternalStringCallbacks*callbacks);public:staticinlineJSExternalString*new_(JSContext*cx,constJS::Latin1Char*chars,size_tlength,constJSExternalStringCallbacks*callbacks);staticinlineJSExternalString*new_(JSContext*cx,constchar16_t*chars,size_tlength,constJSExternalStringCallbacks*callbacks);constJSExternalStringCallbacks*callbacks()const{MOZ_ASSERT(JSString::isExternal());returnd.s.u3.externalCallbacks;}// External chars are never allocated inline or in the nursery, so we can// safely expose this without requiring an AutoCheckCannotGC argument.constJS::Latin1Char*latin1Chars()const{returnrawLatin1Chars();}constchar16_t*twoByteChars()const{returnrawTwoByteChars();}// Only called by the GC for strings with the AllocKind::EXTERNAL_STRING// kind.inlinevoidfinalize(JS::GCContext*gcx);#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddumpOwnRepresentationFields(js::JSONPrinter&json)const;#endif};static_assert(sizeof(JSExternalString)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");classJSAtom:publicJSLinearString{/* Vacuous and therefore unimplemented. */boolisAtom()const=delete;JSAtom&asAtom()const=delete;public:template<typenameCharT>staticinlineJSAtom*newValidLength(JSContext*cx,OwnedChars<CharT>&chars,js::HashNumberhash);/* Returns the PropertyName for this. isIndex() must be false. */inlinejs::PropertyName*asPropertyName();MOZ_ALWAYS_INLINEboolisPermanent()const{returnJSString::isPermanentAtom();}MOZ_ALWAYS_INLINEvoidmakePermanent(){MOZ_ASSERT(JSString::isAtom());setFlagBit(PERMANENT_ATOM_MASK);}MOZ_ALWAYS_INLINEboolisIndex()const{MOZ_ASSERT(JSString::isAtom());mozilla::DebugOnly<uint32_t>index;MOZ_ASSERT(!!(flags()&ATOM_IS_INDEX_BIT)==isIndexSlow(&index));returnflags()&ATOM_IS_INDEX_BIT;}MOZ_ALWAYS_INLINEboolisIndex(uint32_t*index)const{MOZ_ASSERT(JSString::isAtom());if(!isIndex()){returnfalse;}*index=hasIndexValue()?getIndexValue():getIndexSlow();returntrue;}uint32_tgetIndexSlow()const;voidsetIsIndex(uint32_tindex){MOZ_ASSERT(JSString::isAtom());setFlagBit(ATOM_IS_INDEX_BIT);maybeInitializeIndexValue(index,/* allowAtom = */true);}MOZ_ALWAYS_INLINEboolisPinned()const{returnflags()&PINNED_ATOM_BIT;}voidsetPinned(){MOZ_ASSERT(!isPinned());setFlagBit(PINNED_ATOM_BIT);}inlinejs::HashNumberhash()const;template<typenameCharT>staticboollengthFitsInline(size_tlength);#if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)voiddump(js::GenericPrinter&out);voiddump();#endif};namespacejs{classNormalAtom:publicJSAtom{friendclassgc::CellAllocator;protected:staticconstexprsize_tExtensionBytes=js::gc::CellAlignBytes-sizeof(js::HashNumber);charinlineStorage_[ExtensionBytes];HashNumberhash_;// For subclasses to call.explicitNormalAtom(js::HashNumberhash):hash_(hash){}// Out of line atoms, mimicking JSLinearString constructor.template<typenameCharT>NormalAtom(constOwnedChars<CharT>&chars,js::HashNumberhash);public:HashNumberhash()const{returnhash_;}staticconstexprsize_toffsetOfHash(){returnoffsetof(NormalAtom,hash_);}};static_assert(sizeof(NormalAtom)==js::RoundUp(sizeof(JSString)+sizeof(js::HashNumber),js::gc::CellAlignBytes),"NormalAtom must have size of a string + HashNumber, ""aligned to gc::CellAlignBytes");classThinInlineAtom:publicNormalAtom{friendclassgc::CellAllocator;public:staticconstexprsize_tMAX_LENGTH_LATIN1=NUM_INLINE_CHARS_LATIN1+ExtensionBytes/sizeof(JS::Latin1Char);staticconstexprsize_tMAX_LENGTH_TWO_BYTE=NUM_INLINE_CHARS_TWO_BYTE+ExtensionBytes/sizeof(char16_t);#ifdef JS_64BIT// Fat and Thin inline atoms are the same size. Only use fat.staticconstexprboolEverInstantiated=false;#elsestaticconstexprboolEverInstantiated=true;#endifprotected:// Mimicking JSThinInlineString constructors.#ifdef JS_64BITThinInlineAtom(size_tlength,JS::Latin1Char**chars,js::HashNumberhash)=delete;ThinInlineAtom(size_tlength,char16_t**chars,js::HashNumberhash)=delete;#elseThinInlineAtom(size_tlength,JS::Latin1Char**chars,js::HashNumberhash);ThinInlineAtom(size_tlength,char16_t**chars,js::HashNumberhash);#endifpublic:template<typenameCharT>staticboollengthFits(size_tlength){ifconstexpr(sizeof(CharT)==sizeof(JS::Latin1Char)){returnlength<=MAX_LENGTH_LATIN1;}else{returnlength<=MAX_LENGTH_TWO_BYTE;}}};// FatInlineAtom is basically a JSFatInlineString, except it has a hash value in// the last word that reduces the inline char storage.classFatInlineAtom:publicJSAtom{friendclassgc::CellAllocator;// The space available for storing inline characters. It's the same amount of// space as a JSFatInlineString, except we take the hash value out of it.staticconstexprsize_tInlineBytes=sizeof(JSFatInlineString)-sizeof(JSString::Base)-sizeof(js::HashNumber);staticconstexprsize_tExtensionBytes=InlineBytes-JSThinInlineString::InlineBytes;public:staticconstexprsize_tMAX_LENGTH_LATIN1=InlineBytes/sizeof(JS::Latin1Char);staticconstexprsize_tMAX_LENGTH_TWO_BYTE=InlineBytes/sizeof(char16_t);protected:// Silence Clang unused-field warning.charinlineStorage_[ExtensionBytes];HashNumberhash_;// Mimicking JSFatInlineString constructors.explicitFatInlineAtom(size_tlength,JS::Latin1Char**chars,js::HashNumberhash);explicitFatInlineAtom(size_tlength,char16_t**chars,js::HashNumberhash);public:HashNumberhash()const{returnhash_;}inlinevoidfinalize(JS::GCContext*gcx);staticconstexprsize_toffsetOfHash(){static_assert(sizeof(FatInlineAtom)==js::RoundUp(sizeof(JSThinInlineString)+FatInlineAtom::ExtensionBytes+sizeof(HashNumber),gc::CellAlignBytes),"FatInlineAtom must have size of a thin inline string + ""extension bytes if any + HashNumber, ""aligned to gc::CellAlignBytes");returnoffsetof(FatInlineAtom,hash_);}template<typenameCharT>staticboollengthFits(size_tlength){returnlength*sizeof(CharT)<=InlineBytes;}};static_assert(sizeof(FatInlineAtom)==sizeof(JSFatInlineString),"FatInlineAtom must be the same size as a fat inline string");// When an algorithm does not need a string represented as a single linear// array of characters, this range utility may be used to traverse the string a// sequence of linear arrays of characters. This avoids flattening ropes.template<size_tSize=16>classStringSegmentRange{// If malloc() shows up in any profiles from this vector, we can add a new// StackAllocPolicy which stashes a reusable freed-at-gc buffer in the cx.usingStackVector=JS::GCVector<JSString*,Size>;Rooted<StackVector>stack;Rooted<JSLinearString*>cur;boolsettle(JSString*str){while(str->isRope()){JSRope&rope=str->asRope();if(!stack.append(rope.rightChild())){returnfalse;}str=rope.leftChild();}cur=&str->asLinear();returntrue;}public:explicitStringSegmentRange(JSContext*cx):stack(cx,StackVector(cx)),cur(cx){}[[nodiscard]]boolinit(JSString*str){MOZ_ASSERT(stack.empty());returnsettle(str);}boolempty()const{returncur==nullptr;}JSLinearString*front()const{MOZ_ASSERT(!cur->isRope());returncur;}[[nodiscard]]boolpopFront(){MOZ_ASSERT(!empty());if(stack.empty()){cur=nullptr;returntrue;}returnsettle(stack.popCopy());}};// This class should be used in code that manipulates strings off-thread (for// example, Ion compilation). The key difference is that flags are loaded// atomically, preventing data races if flags (especially the pinned atom bit)// are mutated on the main thread. We use private inheritance to avoid// accidentally exposing anything non-thread-safe.classJSOffThreadAtom:privateJSAtom{public:size_tlength()const{returnheaderLengthFieldAtomic();}size_tflags()const{returnheaderFlagsFieldAtomic();}boolempty()const{returnlength()==0;}boolhasLatin1Chars()const{returnflags()&LATIN1_CHARS_BIT;}boolhasTwoByteChars()const{return!(flags()&LATIN1_CHARS_BIT);}boolisAtom()const{returnflags()&ATOM_BIT;}boolisInline()const{returnflags()&INLINE_CHARS_BIT;}boolhasIndexValue()const{returnflags()&INDEX_VALUE_BIT;}boolisIndex()const{returnflags()&ATOM_IS_INDEX_BIT;}boolisFatInline()const{return(flags()&FAT_INLINE_MASK)==FAT_INLINE_MASK;}uint32_tgetIndexValue()const{MOZ_ASSERT(hasIndexValue());returnflags()>>INDEX_VALUE_SHIFT;}boolisIndex(uint32_t*index)const{if(!isIndex()){returnfalse;}*index=hasIndexValue()?getIndexValue():getIndexSlow();returntrue;}uint32_tgetIndexSlow()const;constJS::Latin1Char*latin1Chars(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(hasLatin1Chars());returnisInline()?d.inlineStorageLatin1:d.s.u2.nonInlineCharsLatin1;};constchar16_t*twoByteChars(constJS::AutoRequireNoGC&nogc)const{MOZ_ASSERT(hasTwoByteChars());returnJSLinearString::twoByteChars(nogc);returnisInline()?d.inlineStorageTwoByte:d.s.u2.nonInlineCharsTwoByte;}mozilla::Range<constJS::Latin1Char>latin1Range(constJS::AutoRequireNoGC&nogc)const{returnmozilla::Range<constJS::Latin1Char>(latin1Chars(nogc),length());}mozilla::Range<constchar16_t>twoByteRange(constJS::AutoRequireNoGC&nogc)const{returnmozilla::Range<constchar16_t>(twoByteChars(nogc),length());}char16_tlatin1OrTwoByteChar(size_tindex)const{MOZ_ASSERT(index<length());JS::AutoCheckCannotGCnogc;returnhasLatin1Chars()?latin1Chars(nogc)[index]:twoByteChars(nogc)[index];}inlineHashNumberhash()const{if(isFatInline()){returnreinterpret_cast<constjs::FatInlineAtom*>(this)->hash();}returnreinterpret_cast<constjs::NormalAtom*>(this)->hash();}JSAtom*unwrap(){returnthis;}constJSAtom*unwrap()const{returnthis;}// Should only be used to get an opaque pointer for baking into jitcode.constjs::gc::Cell*raw()const{returnthis;}};}// namespace jsinlinejs::HashNumberJSAtom::hash()const{if(isFatInline()){returnstatic_cast<constjs::FatInlineAtom*>(this)->hash();}returnstatic_cast<constjs::NormalAtom*>(this)->hash();}namespacejs{/* * Represents an atomized string which does not contain an index (that is, an * unsigned 32-bit value). Thus for any PropertyName propname, * ToString(ToUint32(propname)) never equals propname. * * To more concretely illustrate the utility of PropertyName, consider that it * is used to partition, in a type-safe manner, the ways to refer to a * property, as follows: * * - uint32_t indexes, * - PropertyName strings which don't encode uint32_t indexes, * - Symbol, and * - JS::PropertyKey::isVoid. */classPropertyName:publicJSAtom{private:/* Vacuous and therefore unimplemented. */PropertyName*asPropertyName()=delete;};static_assert(sizeof(PropertyName)==sizeof(JSString),"string subclasses must be binary-compatible with JSString");staticMOZ_ALWAYS_INLINEjsidNameToId(PropertyName*name){returnJS::PropertyKey::NonIntAtom(name);}usingPropertyNameVector=JS::GCVector<PropertyName*>;template<typenameCharT>voidCopyChars(CharT*dest,constJSLinearString&str);staticinlineUniqueCharsStringToNewUTF8CharsZ(JSContext*cx,JSString&str){JS::AutoCheckCannotGCnogc;JSLinearString*linear=str.ensureLinear(cx);if(!linear){returnnullptr;}returnUniqueChars(linear->hasLatin1Chars()?JS::CharsToNewUTF8CharsZ(cx,linear->latin1Range(nogc)).c_str():JS::CharsToNewUTF8CharsZ(cx,linear->twoByteRange(nogc)).c_str());}template<typenameCharT>externJSString::OwnedChars<CharT>AllocAtomCharsValidLength(JSContext*cx,size_tlength);/** * Allocate a string with the given contents. If |allowGC == CanGC|, this may * trigger a GC. */template<js::AllowGCallowGC,typenameCharT>externJSLinearString*NewString(JSContext*cx,UniquePtr<CharT[],JS::FreePolicy>chars,size_tlength,js::gc::Heapheap=js::gc::Heap::Default);/* Like NewString, but doesn't try to deflate to Latin1. */template<js::AllowGCallowGC,typenameCharT>externJSLinearString*NewStringDontDeflate(JSContext*cx,UniquePtr<CharT[],JS::FreePolicy>chars,size_tlength,js::gc::Heapheap=js::gc::Heap::Default);/* This may return a static string/atom or an inline string. */externJSLinearString*NewDependentString(JSContext*cx,JSString*base,size_tstart,size_tlength,js::gc::Heapheap=js::gc::Heap::Default);/* As above, but give an option to not contract the chain of base strings, inorder to create messier situations for testing (some of which may not bepossible in practice). */externJSLinearString*NewDependentStringForTesting(JSContext*cx,JSString*base,size_tstart,size_tlength,JS::ContractBaseChaincontract,js::gc::Heapheap);/* Take ownership of an array of Latin1Chars. */externJSLinearString*NewLatin1StringZ(JSContext*cx,UniqueCharschars,js::gc::Heapheap=js::gc::Heap::Default);/* Copy a counted string and GC-allocate a descriptor for it. */template<js::AllowGCallowGC,typenameCharT>externJSLinearString*NewStringCopyN(JSContext*cx,constCharT*s,size_tn,js::gc::Heapheap=js::gc::Heap::Default);template<js::AllowGCallowGC>inlineJSLinearString*NewStringCopyN(JSContext*cx,constchar*s,size_tn,js::gc::Heapheap=js::gc::Heap::Default){returnNewStringCopyN<allowGC>(cx,reinterpret_cast<constLatin1Char*>(s),n,heap);}template<typenameCharT>externJSAtom*NewAtomCopyNMaybeDeflateValidLength(JSContext*cx,constCharT*s,size_tn,js::HashNumberhash);template<typenameCharT>externJSAtom*NewAtomCopyNDontDeflateValidLength(JSContext*cx,constCharT*s,size_tn,js::HashNumberhash);/* Copy a counted string and GC-allocate a descriptor for it. */template<js::AllowGCallowGC,typenameCharT>inlineJSLinearString*NewStringCopy(JSContext*cx,mozilla::Span<constCharT>s,js::gc::Heapheap=js::gc::Heap::Default){returnNewStringCopyN<allowGC>(cx,s.data(),s.size(),heap);}/* Copy a counted string and GC-allocate a descriptor for it. */template<js::AllowGCallowGC,typenameCharT,typenamestd::enable_if_t<!std::is_same_v<CharT,unsignedchar>>*=nullptr>inlineJSLinearString*NewStringCopy(JSContext*cx,std::basic_string_view<CharT>s,js::gc::Heapheap=js::gc::Heap::Default){returnNewStringCopyN<allowGC>(cx,s.data(),s.size(),heap);}/* Like NewStringCopyN, but doesn't try to deflate to Latin1. */template<js::AllowGCallowGC,typenameCharT>externJSLinearString*NewStringCopyNDontDeflate(JSContext*cx,constCharT*s,size_tn,js::gc::Heapheap=js::gc::Heap::Default);template<js::AllowGCallowGC,typenameCharT>externJSLinearString*NewStringCopyNDontDeflateNonStaticValidLength(JSContext*cx,constCharT*s,size_tn,js::gc::Heapheap=js::gc::Heap::Default);/* Copy a C string and GC-allocate a descriptor for it. */template<js::AllowGCallowGC>inlineJSLinearString*NewStringCopyZ(JSContext*cx,constchar16_t*s,js::gc::Heapheap=js::gc::Heap::Default){returnNewStringCopyN<allowGC>(cx,s,js_strlen(s),heap);}template<js::AllowGCallowGC>inlineJSLinearString*NewStringCopyZ(JSContext*cx,constchar*s,js::gc::Heapheap=js::gc::Heap::Default){returnNewStringCopyN<allowGC>(cx,s,strlen(s),heap);}externJSLinearString*NewStringCopyUTF8N(JSContext*cx,constJS::UTF8Chars&utf8,JS::SmallestEncodingencoding,js::gc::Heapheap=js::gc::Heap::Default);externJSLinearString*NewStringCopyUTF8N(JSContext*cx,constJS::UTF8Chars&utf8,js::gc::Heapheap=js::gc::Heap::Default);inlineJSLinearString*NewStringCopyUTF8Z(JSContext*cx,constJS::ConstUTF8CharsZutf8,js::gc::Heapheap=js::gc::Heap::Default){returnNewStringCopyUTF8N(cx,JS::UTF8Chars(utf8.c_str(),strlen(utf8.c_str())),heap);}template<typenameCharT>JSString*NewMaybeExternalString(JSContext*cx,constCharT*s,size_tn,constJSExternalStringCallbacks*callbacks,bool*allocatedExternal,js::gc::Heapheap=js::gc::Heap::Default);static_assert(sizeof(HashNumber)==4);template<AllowGCallowGC>externJSString*ConcatStrings(JSContext*cx,typenameMaybeRooted<JSString*,allowGC>::HandleTypeleft,typenameMaybeRooted<JSString*,allowGC>::HandleTyperight,js::gc::Heapheap=js::gc::Heap::Default);/* * Test if strings are equal. The caller can call the function even if str1 * or str2 are not GC-allocated things. */externboolEqualStrings(JSContext*cx,JSString*str1,JSString*str2,bool*result);/* Use the infallible method instead! */externboolEqualStrings(JSContext*cx,JSLinearString*str1,JSLinearString*str2,bool*result)=delete;/* EqualStrings is infallible on linear strings. */externboolEqualStrings(constJSLinearString*str1,constJSLinearString*str2);/** * Compare two strings that are known to be the same length. * Exposed for the JITs; for ordinary uses, EqualStrings() is more sensible. * * The caller must have checked for the following cases that can be handled * efficiently without requiring a character comparison: * - str1 == str2 * - str1->length() != str2->length() * - str1->isAtom() && str2->isAtom() */externboolEqualChars(constJSLinearString*str1,constJSLinearString*str2);/* * Return less than, equal to, or greater than zero depending on whether * `s1[0..len1]` is less than, equal to, or greater than `s2`. */externint32_tCompareChars(constchar16_t*s1,size_tlen1,constJSLinearString*s2);/* * Compare two strings, like CompareChars, but store the result in `*result`. * This flattens the strings and therefore can fail. */externboolCompareStrings(JSContext*cx,JSString*str1,JSString*str2,int32_t*result);/* * Compare two strings, like CompareChars. */externint32_tCompareStrings(constJSLinearString*str1,constJSLinearString*str2);/* * Compare two strings, like CompareChars. Can be called off-thread. */externint32_tCompareStrings(constJSOffThreadAtom*str1,constJSOffThreadAtom*str2);/** * Return true if the string contains only ASCII characters. */externboolStringIsAscii(constJSLinearString*str);/* * Return true if the string matches the given sequence of ASCII bytes. */externboolStringEqualsAscii(constJSLinearString*str,constchar*asciiBytes);/* * Return true if the string matches the given sequence of ASCII * bytes. The sequence of ASCII bytes must have length "length". The * length should not include the trailing null, if any. */externboolStringEqualsAscii(constJSLinearString*str,constchar*asciiBytes,size_tlength);template<size_tN>boolStringEqualsLiteral(constJSLinearString*str,constchar(&asciiBytes)[N]){MOZ_ASSERT(asciiBytes[N-1]=='\0');returnStringEqualsAscii(str,asciiBytes,N-1);}externintStringFindPattern(constJSLinearString*text,constJSLinearString*pat,size_tstart);/** * Return true if the string contains a pattern at |start|. * * Precondition: `text` is long enough that this might be true; * that is, it has at least `start + pat->length()` characters. */externboolHasSubstringAt(constJSLinearString*text,constJSLinearString*pat,size_tstart);/* * Computes |str|'s substring for the range [beginInt, beginInt + lengthInt). * Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden * and constitute API misuse. */JSString*SubstringKernel(JSContext*cx,HandleStringstr,int32_tbeginInt,int32_tlengthInt);inlinejs::HashNumberHashStringChars(constJSLinearString*str){JS::AutoCheckCannotGCnogc;size_tlen=str->length();returnstr->hasLatin1Chars()?mozilla::HashString(str->latin1Chars(nogc),len):mozilla::HashString(str->twoByteChars(nogc),len);}/** * Allocate string characters when the final string length is known in advance. */template<typenameCharT>classMOZ_NON_PARAMStringChars{staticconstexprsize_tInlineLength=std::is_same_v<CharT,JS::Latin1Char>?JSFatInlineString::MAX_LENGTH_LATIN1:JSFatInlineString::MAX_LENGTH_TWO_BYTE;CharTinlineChars_[InlineLength];Rooted<JSString::OwnedChars<CharT>>ownedChars_;#ifdef DEBUG// In debug mode, we keep track of the requested string lengths to ensure all// methods are called in the correct order and with the expected argument// values.size_tlastRequestedLength_=0;voidassertValidRequest(size_texpectedLastLength,size_tlength){MOZ_ASSERT(length>=expectedLastLength,"cannot shrink requested length");MOZ_ASSERT(lastRequestedLength_==expectedLastLength);lastRequestedLength_=length;}#elsevoidassertValidRequest(size_texpectedLastLength,size_tlength){}#endifpublic:explicitStringChars(JSContext*cx):ownedChars_(cx){}/** * Return a raw pointer to the string characters. The pointer can point to * nursery allocated memory, so the caller should ensure no GC can happen * while using this pointer. */CharT*data(constJS::AutoRequireNoGC&){returnownedChars_?ownedChars_.data():inlineChars_;}/** * Escape hatch when it's not possible to call `data(nogc)`. Use with caution! */CharT*unsafeData(){returnownedChars_?ownedChars_.data():inlineChars_;}/** * Prepare for writing |length| characters. Allocates iff `length` exceeds the * inline storage of this class. */boolmaybeAlloc(JSContext*cx,size_tlength,gc::Heapheap=gc::Heap::Default);/** * Increase the string characters storage. Allocates iff `newLength` exceeds * the inline storage of this class. */boolmaybeRealloc(JSContext*cx,size_toldLength,size_tnewLength,gc::Heapheap=gc::Heap::Default);/** * Build the result string. Does not deflate two-byte characters if all * characters fit into Latin-1. */template<AllowGCallowGC>JSLinearString*toStringDontDeflate(JSContext*cx,size_tlength,gc::Heapheap=gc::Heap::Default);/** * Build the result string. Does not deflate two-byte characters if all * characters fit into Latin-1. And does not check static strings. */template<AllowGCallowGC>JSLinearString*toStringDontDeflateNonStatic(JSContext*cx,size_tlength,gc::Heapheap=gc::Heap::Default);};/** * Allocate atom characters when the final string length is known in advance. */template<typenameCharT>classMOZ_NON_PARAMAtomStringChars{staticconstexprsize_tInlineLength=std::is_same_v<CharT,JS::Latin1Char>?JSFatInlineString::MAX_LENGTH_LATIN1:JSFatInlineString::MAX_LENGTH_TWO_BYTE;CharTinlineChars_[InlineLength];UniquePtr<CharT[],JS::FreePolicy>mallocChars_;#ifdef DEBUG// In debug mode, we keep track of the requested string lengths to ensure all// methods are called in the correct order and with the expected argument// values.size_tlastRequestedLength_=0;voidassertValidRequest(size_texpectedLastLength,size_tlength){MOZ_ASSERT(length>=expectedLastLength,"cannot shrink requested length");MOZ_ASSERT(lastRequestedLength_==expectedLastLength);lastRequestedLength_=length;}#elsevoidassertValidRequest(size_texpectedLastLength,size_tlength){}#endifpublic:/** * Return a raw pointer to the string characters. */CharT*data(){returnmallocChars_?mallocChars_.get():inlineChars_;}/** * Prepare for writing |length| characters. Allocates iff `length` exceeds the * inline storage of this class. */boolmaybeAlloc(JSContext*cx,size_tlength);/** * Build the result atom string. */JSAtom*toAtom(JSContext*cx,size_tlength);};/*** Conversions ************************************************************//* * Convert a string to a printable C string. * * Asserts if the input contains any non-ASCII characters. */UniqueCharsEncodeAscii(JSContext*cx,JSString*str);/* * Convert a string to a printable C string. */UniqueCharsEncodeLatin1(JSContext*cx,JSString*str);enumclassIdToPrintableBehavior:bool{/* * Request the printable representation of an identifier. */IdIsIdentifier,/* * Request the printable representation of a property key. */IdIsPropertyKey};/* * Convert a jsid to a printable C string encoded in UTF-8. */externUniqueCharsIdToPrintableUTF8(JSContext*cx,HandleIdid,IdToPrintableBehaviorbehavior);/* * Convert a non-string value to a string, returning null after reporting an * error, otherwise returning a new string reference. */template<AllowGCallowGC>externJSString*ToStringSlow(JSContext*cx,typenameMaybeRooted<Value,allowGC>::HandleTypearg);/* * Convert the given value to a string. This method includes an inline * fast-path for the case where the value is already a string; if the value is * known not to be a string, use ToStringSlow instead. */template<AllowGCallowGC>staticMOZ_ALWAYS_INLINEJSString*ToString(JSContext*cx,JS::HandleValuev){if(v.isString()){returnv.toString();}returnToStringSlow<allowGC>(cx,v);}/* * This function implements E-262-3 section 9.8, toString. Convert the given * value to a string of characters appended to the given builder. On error, the * passed builder may have partial results appended. */inlineboolValueToStringBuilder(JSContext*cx,constValue&v,StringBuilder&sb);}/* namespace js */MOZ_ALWAYS_INLINEboolJSString::getChar(JSContext*cx,size_tindex,char16_t*code){MOZ_ASSERT(index<length());/* * Optimization for one level deep ropes. * This is common for the following pattern: * * while() { * text = text.substr(0, x) + "bla" + text.substr(x) * test.charCodeAt(x + 1) * } * * Note: keep this in sync with MacroAssembler::loadStringChar and * CanAttachStringChar. */JSString*str;if(isRope()){JSRope*rope=&asRope();if(uint32_t(index)<rope->leftChild()->length()){str=rope->leftChild();}else{str=rope->rightChild();index-=rope->leftChild()->length();}}else{str=this;}if(!str->ensureLinear(cx)){returnfalse;}*code=str->asLinear().latin1OrTwoByteChar(index);returntrue;}MOZ_ALWAYS_INLINEboolJSString::getCodePoint(JSContext*cx,size_tindex,char32_t*code){// C++ implementation of https://tc39.es/ecma262/#sec-codepointatsize_tsize=length();MOZ_ASSERT(index<size);char16_tfirst;if(!getChar(cx,index,&first)){returnfalse;}if(!js::unicode::IsLeadSurrogate(first)||index+1==size){*code=first;returntrue;}char16_tsecond;if(!getChar(cx,index+1,&second)){returnfalse;}if(!js::unicode::IsTrailSurrogate(second)){*code=first;returntrue;}*code=js::unicode::UTF16Decode(first,second);returntrue;}MOZ_ALWAYS_INLINEJSLinearString*JSString::ensureLinear(JSContext*cx){returnisLinear()?&asLinear():asRope().flatten(cx);}inlineJSLinearString*JSString::base()const{MOZ_ASSERT(hasBase());MOZ_ASSERT_IF(!isAtomRef(),!d.s.u3.base->isInline());MOZ_ASSERT(d.s.u3.base->assertIsValidBase());if(isAtomRef()){returnstatic_cast<JSLinearString*>(d.s.u3.atom);}returnd.s.u3.base;}inlineJSAtom*JSString::atom()const{MOZ_ASSERT(isAtomRef());returnd.s.u3.atom;}inlineJSLinearString*JSString::nurseryBaseOrRelocOverlay()const{MOZ_ASSERT(hasBase());returnd.s.u3.base;}inlineboolJSString::canOwnDependentChars()const{// A string that could own the malloced chars used by another (dependent)// string. It will not have a base and must be linear and non-inline.returnisLinear()&&!isInline()&&!hasBase();}template<>MOZ_ALWAYS_INLINEconstchar16_t*JSLinearString::nonInlineChars(constJS::AutoRequireNoGC&nogc)const{returnnonInlineTwoByteChars(nogc);}template<>MOZ_ALWAYS_INLINEconstJS::Latin1Char*JSLinearString::nonInlineChars(constJS::AutoRequireNoGC&nogc)const{returnnonInlineLatin1Chars(nogc);}template<>MOZ_ALWAYS_INLINEconstchar16_t*JSLinearString::chars(constJS::AutoRequireNoGC&nogc)const{returnrawTwoByteChars();}template<>MOZ_ALWAYS_INLINEconstJS::Latin1Char*JSLinearString::chars(constJS::AutoRequireNoGC&nogc)const{returnrawLatin1Chars();}template<>MOZ_ALWAYS_INLINEjs::UniquePtr<JS::Latin1Char[],JS::FreePolicy>JSRope::copyChars<JS::Latin1Char>(JSContext*maybecx,arena_id_tdestArenaId)const{returncopyLatin1Chars(maybecx,destArenaId);}template<>MOZ_ALWAYS_INLINEJS::UniqueTwoByteCharsJSRope::copyChars<char16_t>(JSContext*maybecx,arena_id_tdestArenaId)const{returncopyTwoByteChars(maybecx,destArenaId);}template<>MOZ_ALWAYS_INLINEboolJSThinInlineString::lengthFits<JS::Latin1Char>(size_tlength){returnlength<=MAX_LENGTH_LATIN1;}template<>MOZ_ALWAYS_INLINEboolJSThinInlineString::lengthFits<char16_t>(size_tlength){returnlength<=MAX_LENGTH_TWO_BYTE;}template<>MOZ_ALWAYS_INLINEboolJSFatInlineString::lengthFits<JS::Latin1Char>(size_tlength){static_assert((INLINE_EXTENSION_CHARS_LATIN1*sizeof(char))%js::gc::CellAlignBytes==0,"fat inline strings' Latin1 characters don't exactly ""fill subsequent cells and thus are wasteful");static_assert(MAX_LENGTH_LATIN1==(sizeof(JSFatInlineString)-offsetof(JSFatInlineString,d.inlineStorageLatin1))/sizeof(char),"MAX_LENGTH_LATIN1 must be one less than inline Latin1 ""storage count");returnlength<=MAX_LENGTH_LATIN1;}template<>MOZ_ALWAYS_INLINEboolJSFatInlineString::lengthFits<char16_t>(size_tlength){static_assert((INLINE_EXTENSION_CHARS_TWO_BYTE*sizeof(char16_t))%js::gc::CellAlignBytes==0,"fat inline strings' char16_t characters don't exactly ""fill subsequent cells and thus are wasteful");static_assert(MAX_LENGTH_TWO_BYTE==(sizeof(JSFatInlineString)-offsetof(JSFatInlineString,d.inlineStorageTwoByte))/sizeof(char16_t),"MAX_LENGTH_TWO_BYTE must be one less than inline ""char16_t storage count");returnlength<=MAX_LENGTH_TWO_BYTE;}template<>MOZ_ALWAYS_INLINEboolJSInlineString::lengthFits<JS::Latin1Char>(size_tlength){// If it fits in a fat inline string, it fits in any inline string.returnJSFatInlineString::lengthFits<JS::Latin1Char>(length);}template<>MOZ_ALWAYS_INLINEboolJSInlineString::lengthFits<char16_t>(size_tlength){// If it fits in a fat inline string, it fits in any inline string.returnJSFatInlineString::lengthFits<char16_t>(length);}template<>MOZ_ALWAYS_INLINEbooljs::ThinInlineAtom::lengthFits<JS::Latin1Char>(size_tlength){returnlength<=MAX_LENGTH_LATIN1;}template<>MOZ_ALWAYS_INLINEbooljs::ThinInlineAtom::lengthFits<char16_t>(size_tlength){returnlength<=MAX_LENGTH_TWO_BYTE;}template<>MOZ_ALWAYS_INLINEbooljs::FatInlineAtom::lengthFits<JS::Latin1Char>(size_tlength){returnlength<=MAX_LENGTH_LATIN1;}template<>MOZ_ALWAYS_INLINEbooljs::FatInlineAtom::lengthFits<char16_t>(size_tlength){returnlength<=MAX_LENGTH_TWO_BYTE;}template<>MOZ_ALWAYS_INLINEboolJSAtom::lengthFitsInline<JS::Latin1Char>(size_tlength){// If it fits in a fat inline atom, it fits in any inline atom.returnjs::FatInlineAtom::lengthFits<JS::Latin1Char>(length);}template<>MOZ_ALWAYS_INLINEboolJSAtom::lengthFitsInline<char16_t>(size_tlength){// If it fits in a fat inline atom, it fits in any inline atom.returnjs::FatInlineAtom::lengthFits<char16_t>(length);}template<>MOZ_ALWAYS_INLINEvoidJSString::setNonInlineChars(constchar16_t*chars,boolusesStringBuffer){// Check that the new buffer is located in the StringBufferArenaif(!(isAtomRef()&&atom()->isInline())){checkStringCharsArena(chars,usesStringBuffer);}d.s.u2.nonInlineCharsTwoByte=chars;}template<>MOZ_ALWAYS_INLINEvoidJSString::setNonInlineChars(constJS::Latin1Char*chars,boolusesStringBuffer){// Check that the new buffer is located in the StringBufferArenaif(!(isAtomRef()&&atom()->isInline())){checkStringCharsArena(chars,usesStringBuffer);}d.s.u2.nonInlineCharsLatin1=chars;}MOZ_ALWAYS_INLINEconstJS::Latin1Char*JSLinearString::rawLatin1Chars()const{MOZ_ASSERT(JSString::isLinear());MOZ_ASSERT(hasLatin1Chars());returnisInline()?d.inlineStorageLatin1:d.s.u2.nonInlineCharsLatin1;}MOZ_ALWAYS_INLINEconstchar16_t*JSLinearString::rawTwoByteChars()const{MOZ_ASSERT(JSString::isLinear());MOZ_ASSERT(hasTwoByteChars());returnisInline()?d.inlineStorageTwoByte:d.s.u2.nonInlineCharsTwoByte;}inlinejs::PropertyName*JSAtom::asPropertyName(){MOZ_ASSERT(!isIndex());returnstatic_cast<js::PropertyName*>(this);}inlineboolJSLinearString::isIndex(uint32_t*indexp)const{MOZ_ASSERT(JSString::isLinear());if(isAtom()){returnasAtom().isIndex(indexp);}if(JSString::hasIndexValue()){*indexp=getIndexValue();returntrue;}returnisIndexSlow(indexp);}namespacejs{namespacegc{template<>inlineJSString*Cell::as<JSString>(){MOZ_ASSERT(is<JSString>());returnreinterpret_cast<JSString*>(this);}template<>inlineJSString*TenuredCell::as<JSString>(){MOZ_ASSERT(is<JSString>());returnreinterpret_cast<JSString*>(this);}}// namespace gc}// namespace js#endif /* vm_StringType_h */