tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StringType-inl.h (29157B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef vm_StringType_inl_h
      8 #define vm_StringType_inl_h
      9 
     10 #include "vm/StringType.h"
     11 
     12 #include "mozilla/PodOperations.h"
     13 #include "mozilla/Range.h"
     14 #include "mozilla/StringBuffer.h"
     15 
     16 #include "gc/GCEnum.h"
     17 #include "gc/MaybeRooted.h"
     18 #include "gc/StoreBuffer.h"
     19 #include "js/UniquePtr.h"
     20 #include "vm/StaticStrings.h"
     21 
     22 #include "gc/GCContext-inl.h"
     23 #include "gc/Marking-inl.h"
     24 #include "gc/StoreBuffer-inl.h"
     25 #include "vm/JSContext-inl.h"
     26 
     27 namespace js {
     28 
     29 // Allocate a thin inline string if possible, and a fat inline string if not.
     30 template <AllowGC allowGC, typename CharT>
     31 static MOZ_ALWAYS_INLINE JSInlineString* AllocateInlineString(
     32    JSContext* cx, size_t len, CharT** chars, js::gc::Heap heap) {
     33  MOZ_ASSERT(JSInlineString::lengthFits<CharT>(len));
     34 
     35  if (JSThinInlineString::lengthFits<CharT>(len)) {
     36    return cx->newCell<JSThinInlineString, allowGC>(heap, len, chars);
     37  }
     38  return cx->newCell<JSFatInlineString, allowGC>(heap, len, chars);
     39 }
     40 
     41 template <typename CharT>
     42 static MOZ_ALWAYS_INLINE JSAtom* AllocateInlineAtom(JSContext* cx, size_t len,
     43                                                    CharT** chars,
     44                                                    js::HashNumber hash) {
     45  MOZ_ASSERT(JSAtom::lengthFitsInline<CharT>(len));
     46  if constexpr (js::ThinInlineAtom::EverInstantiated) {
     47    if (js::ThinInlineAtom::lengthFits<CharT>(len)) {
     48      return cx->newCell<js::ThinInlineAtom, js::NoGC>(len, chars, hash);
     49    }
     50  }
     51  return cx->newCell<js::FatInlineAtom, js::NoGC>(len, chars, hash);
     52 }
     53 
     54 // Create a thin inline string if possible, and a fat inline string if not.
     55 template <AllowGC allowGC, typename CharT>
     56 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(
     57    JSContext* cx, mozilla::Range<const CharT> chars,
     58    js::gc::Heap heap = js::gc::Heap::Default) {
     59  /*
     60   * Don't bother trying to find a static atom; measurement shows that not
     61   * many get here (for one, Atomize is catching them).
     62   */
     63 
     64  size_t len = chars.length();
     65  CharT* storage;
     66  JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage, heap);
     67  if (!str) {
     68    return nullptr;
     69  }
     70 
     71  mozilla::PodCopy(storage, chars.begin().get(), len);
     72  return str;
     73 }
     74 
     75 // Create a thin inline string if possible, and a fat inline string if not.
     76 template <AllowGC allowGC, typename CharT, size_t N>
     77 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(
     78    JSContext* cx, const CharT (&chars)[N], size_t len,
     79    js::gc::Heap heap = js::gc::Heap::Default) {
     80  MOZ_ASSERT(len <= N);
     81 
     82  /*
     83   * Don't bother trying to find a static atom; measurement shows that not
     84   * many get here (for one, Atomize is catching them).
     85   */
     86 
     87  CharT* storage;
     88  JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage, heap);
     89  if (!str) {
     90    return nullptr;
     91  }
     92 
     93  if (JSThinInlineString::lengthFits<CharT>(len)) {
     94    constexpr size_t MaxLength = std::is_same_v<CharT, Latin1Char>
     95                                     ? JSThinInlineString::MAX_LENGTH_LATIN1
     96                                     : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
     97 
     98    // memcpy with a constant length can be optimized more easily by compilers.
     99    constexpr size_t toCopy = std::min(N, MaxLength) * sizeof(CharT);
    100    std::memcpy(storage, chars, toCopy);
    101  } else {
    102    constexpr size_t MaxLength = std::is_same_v<CharT, Latin1Char>
    103                                     ? JSFatInlineString::MAX_LENGTH_LATIN1
    104                                     : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
    105 
    106    // memcpy with a constant length can be optimized more easily by compilers.
    107    constexpr size_t toCopy = std::min(N, MaxLength) * sizeof(CharT);
    108    std::memcpy(storage, chars, toCopy);
    109  }
    110  return str;
    111 }
    112 
    113 template <typename CharT>
    114 static MOZ_ALWAYS_INLINE JSAtom* NewInlineAtom(JSContext* cx,
    115                                               const CharT* chars,
    116                                               size_t length,
    117                                               js::HashNumber hash) {
    118  CharT* storage;
    119  JSAtom* str = AllocateInlineAtom(cx, length, &storage, hash);
    120  if (!str) {
    121    return nullptr;
    122  }
    123 
    124  mozilla::PodCopy(storage, chars, length);
    125  return str;
    126 }
    127 
    128 // Create a thin inline string if possible, and a fat inline string if not.
    129 template <typename CharT>
    130 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(
    131    JSContext* cx, Handle<JSLinearString*> base, size_t start, size_t length,
    132    js::gc::Heap heap) {
    133  MOZ_ASSERT(JSInlineString::lengthFits<CharT>(length));
    134 
    135  CharT* chars;
    136  JSInlineString* s = AllocateInlineString<CanGC>(cx, length, &chars, heap);
    137  if (!s) {
    138    return nullptr;
    139  }
    140 
    141  JS::AutoCheckCannotGC nogc;
    142  mozilla::PodCopy(chars, base->chars<CharT>(nogc) + start, length);
    143  return s;
    144 }
    145 
    146 template <typename CharT>
    147 static MOZ_ALWAYS_INLINE JSLinearString* TryEmptyOrStaticString(
    148    JSContext* cx, const CharT* chars, size_t n) {
    149  // Measurements on popular websites indicate empty strings are pretty common
    150  // and most strings with length 1 or 2 are in the StaticStrings table. For
    151  // length 3 strings that's only about 1%, so we check n <= 2.
    152  if (n <= 2) {
    153    if (n == 0) {
    154      return cx->emptyString();
    155    }
    156 
    157    if (JSLinearString* str = cx->staticStrings().lookup(chars, n)) {
    158      return str;
    159    }
    160  }
    161 
    162  return nullptr;
    163 }
    164 
    165 } /* namespace js */
    166 
    167 template <typename CharT>
    168 JSString::OwnedChars<CharT>::OwnedChars(CharT* chars, size_t length, Kind kind)
    169    : chars_(chars, length), kind_(kind) {
    170  MOZ_ASSERT(kind != Kind::Uninitialized);
    171  MOZ_ASSERT(length > 0);
    172  MOZ_ASSERT(chars);
    173 #ifdef DEBUG
    174  bool inNursery = js::TlsContext.get()->nursery().isInside(chars);
    175  MOZ_ASSERT((kind == Kind::Nursery) == inNursery);
    176 #endif
    177 }
    178 
    179 template <typename CharT>
    180 JSString::OwnedChars<CharT>::OwnedChars(JSString::OwnedChars<CharT>&& other)
    181    : chars_(other.chars_), kind_(other.kind_) {
    182  other.release();
    183 }
    184 
    185 template <typename CharT>
    186 JSString::OwnedChars<CharT>& JSString::OwnedChars<CharT>::operator=(
    187    JSString::OwnedChars<CharT>&& other) {
    188  reset();
    189  chars_ = other.chars_;
    190  kind_ = other.kind_;
    191  other.release();
    192  return *this;
    193 }
    194 
    195 template <typename CharT>
    196 CharT* JSString::OwnedChars<CharT>::release() {
    197  CharT* chars = chars_.data();
    198  chars_ = {};
    199  kind_ = Kind::Uninitialized;
    200  return chars;
    201 }
    202 
    203 template <typename CharT>
    204 void JSString::OwnedChars<CharT>::reset() {
    205  switch (kind_) {
    206    case Kind::Uninitialized:
    207    case Kind::Nursery:
    208      break;
    209    case Kind::Malloc:
    210      js_free(chars_.data());
    211      break;
    212    case Kind::StringBuffer:
    213      mozilla::StringBuffer::FromData(chars_.data())->Release();
    214      break;
    215  }
    216  chars_ = {};
    217  kind_ = Kind::Uninitialized;
    218 }
    219 
    220 template <typename CharT>
    221 void JSString::OwnedChars<CharT>::ensureNonNursery() {
    222  if (kind_ != Kind::Nursery) {
    223    return;
    224  }
    225 
    226  js::AutoEnterOOMUnsafeRegion oomUnsafe;
    227  CharT* oldPtr = data();
    228  size_t length = chars_.Length();
    229  CharT* ptr = js_pod_arena_malloc<CharT>(js::StringBufferArena, length);
    230  if (!ptr) {
    231    oomUnsafe.crash(chars_.size(), "moving nursery buffer to heap");
    232  }
    233  mozilla::PodCopy(ptr, oldPtr, length);
    234  chars_ = mozilla::Span<CharT>(ptr, length);
    235  kind_ = Kind::Malloc;
    236 }
    237 
    238 template <typename CharT>
    239 JSString::OwnedChars<CharT>::OwnedChars(
    240    js::UniquePtr<CharT[], JS::FreePolicy>&& chars, size_t length)
    241    : OwnedChars(chars.release(), length, Kind::Malloc) {}
    242 
    243 template <typename CharT>
    244 JSString::OwnedChars<CharT>::OwnedChars(RefPtr<mozilla::StringBuffer>&& buffer,
    245                                        size_t length)
    246    : OwnedChars(static_cast<CharT*>(buffer->Data()), length,
    247                 Kind::StringBuffer) {
    248  // Transfer the reference from |buffer| to this OwnedChars.
    249  mozilla::StringBuffer* buf;
    250  buffer.forget(&buf);
    251 }
    252 
    253 MOZ_ALWAYS_INLINE bool JSString::validateLength(JSContext* cx, size_t length) {
    254  return validateLengthInternal<js::CanGC>(cx, length);
    255 }
    256 
    257 template <js::AllowGC allowGC>
    258 MOZ_ALWAYS_INLINE bool JSString::validateLengthInternal(JSContext* cx,
    259                                                        size_t length) {
    260  if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) {
    261    if constexpr (allowGC) {
    262      js::ReportOversizedAllocation(cx, JSMSG_ALLOC_OVERFLOW);
    263    }
    264    return false;
    265  }
    266 
    267  return true;
    268 }
    269 
    270 template <>
    271 MOZ_ALWAYS_INLINE const char16_t* JSString::nonInlineCharsRaw() const {
    272  return d.s.u2.nonInlineCharsTwoByte;
    273 }
    274 
    275 template <>
    276 MOZ_ALWAYS_INLINE const JS::Latin1Char* JSString::nonInlineCharsRaw() const {
    277  return d.s.u2.nonInlineCharsLatin1;
    278 }
    279 
    280 bool JSString::ownsMallocedChars() const {
    281  if (!hasOutOfLineChars() || asLinear().hasStringBuffer()) {
    282    return false;
    283  }
    284 
    285  js::gc::StoreBuffer* sb = storeBuffer();
    286  if (!sb) {
    287    // Tenured strings always own out-of-line chars.
    288    return true;
    289  }
    290 
    291  // Return whether the chars are malloced. Note: this allows the data to be in
    292  // a different nursery chunk than the Cell itself, at the performance cost of
    293  // iterating over all chunks.
    294  return !sb->nursery().isInside(asLinear().nonInlineCharsRaw());
    295 }
    296 
    297 template <typename CharT>
    298 inline size_t JSLinearString::maybeMallocCharsOnPromotion(
    299    js::Nursery* nursery) {
    300  const void** chars;
    301  if constexpr (std::is_same_v<CharT, char16_t>) {
    302    chars = reinterpret_cast<const void**>(&d.s.u2.nonInlineCharsTwoByte);
    303  } else {
    304    chars = reinterpret_cast<const void**>(&d.s.u2.nonInlineCharsLatin1);
    305  }
    306 
    307  size_t bytesUsed = length() * sizeof(CharT);
    308  size_t bytesCapacity =
    309      isExtensible() ? (asExtensible().capacity() * sizeof(CharT)) : bytesUsed;
    310  MOZ_ASSERT(bytesUsed <= bytesCapacity);
    311 
    312  if (nursery->maybeMoveNurseryOrMallocBufferOnPromotion(
    313          const_cast<void**>(chars), this, bytesUsed, bytesCapacity,
    314          js::MemoryUse::StringContents,
    315          js::StringBufferArena) == js::Nursery::BufferMoved) {
    316    MOZ_ASSERT(allocSize() == bytesCapacity);
    317    return bytesCapacity;
    318  }
    319 
    320  return 0;
    321 }
    322 
    323 inline size_t JSLinearString::allocSize() const {
    324  MOZ_ASSERT(ownsMallocedChars() || hasStringBuffer());
    325 
    326  size_t charSize =
    327      hasLatin1Chars() ? sizeof(JS::Latin1Char) : sizeof(char16_t);
    328  size_t count = isExtensible() ? asExtensible().capacity() : length();
    329  return count * charSize;
    330 }
    331 
    332 inline size_t JSString::allocSize() const {
    333  if (ownsMallocedChars() || hasStringBuffer()) {
    334    return asLinear().allocSize();
    335  }
    336  return 0;
    337 }
    338 
    339 inline JSRope::JSRope(JSString* left, JSString* right, size_t length) {
    340  // JITs expect rope children aren't empty.
    341  MOZ_ASSERT(!left->empty() && !right->empty());
    342 
    343  // |length| must be the sum of the length of both child nodes.
    344  MOZ_ASSERT(left->length() + right->length() == length);
    345 
    346  // |isLatin1| is set when both children are guaranteed to contain only Latin-1
    347  // characters. Note that flattening either rope child can clear the Latin-1
    348  // flag of that child, so it's possible that a Latin-1 rope can end up with
    349  // both children being two-byte (dependent) strings.
    350  bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars();
    351 
    352  // Do not try to make a rope that could fit inline.
    353  MOZ_ASSERT_IF(!isLatin1, !JSInlineString::lengthFits<char16_t>(length));
    354  MOZ_ASSERT_IF(isLatin1, !JSInlineString::lengthFits<JS::Latin1Char>(length));
    355 
    356  if (isLatin1) {
    357    setLengthAndFlags(length, INIT_ROPE_FLAGS | LATIN1_CHARS_BIT);
    358  } else {
    359    setLengthAndFlags(length, INIT_ROPE_FLAGS);
    360  }
    361  d.s.u2.left = left;
    362  d.s.u3.right = right;
    363 
    364  // Post-barrier by inserting into the whole cell buffer if either
    365  // this -> left or this -> right is a tenured -> nursery edge.
    366  if (isTenured()) {
    367    js::gc::StoreBuffer* sb = left->storeBuffer();
    368    if (!sb) {
    369      sb = right->storeBuffer();
    370    }
    371    if (sb) {
    372      sb->putWholeCell(this);
    373    }
    374  }
    375 }
    376 
    377 template <js::AllowGC allowGC>
    378 MOZ_ALWAYS_INLINE JSRope* JSRope::new_(
    379    JSContext* cx,
    380    typename js::MaybeRooted<JSString*, allowGC>::HandleType left,
    381    typename js::MaybeRooted<JSString*, allowGC>::HandleType right,
    382    size_t length, js::gc::Heap heap) {
    383  if (MOZ_UNLIKELY(!validateLengthInternal<allowGC>(cx, length))) {
    384    return nullptr;
    385  }
    386  return cx->newCell<JSRope, allowGC>(heap, left, right, length);
    387 }
    388 
    389 inline JSDependentString::JSDependentString(JSLinearString* base, size_t start,
    390                                            size_t length) {
    391  MOZ_ASSERT(start + length <= base->length());
    392  JS::AutoCheckCannotGC nogc;
    393  if (base->hasLatin1Chars()) {
    394    setLengthAndFlags(length, INIT_DEPENDENT_FLAGS | LATIN1_CHARS_BIT);
    395    d.s.u2.nonInlineCharsLatin1 = base->latin1Chars(nogc) + start;
    396  } else {
    397    setLengthAndFlags(length, INIT_DEPENDENT_FLAGS);
    398    d.s.u2.nonInlineCharsTwoByte = base->twoByteChars(nogc) + start;
    399  }
    400  base->setDependedOn();
    401  d.s.u3.base = base;
    402  if (isTenured() && !base->isTenured()) {
    403    base->storeBuffer()->putWholeCell(this);
    404  }
    405 }
    406 
    407 template <JS::ContractBaseChain contract>
    408 MOZ_ALWAYS_INLINE JSLinearString* JSDependentString::newImpl_(
    409    JSContext* cx, JSLinearString* baseArg, size_t start, size_t length,
    410    js::gc::Heap heap) {
    411  // Not passed in as a Handle because `base` is reassigned.
    412  JS::Rooted<JSLinearString*> base(cx, baseArg);
    413 
    414  // Do not try to make a dependent string that could fit inline.
    415  MOZ_ASSERT_IF(base->hasTwoByteChars(),
    416                !JSInlineString::lengthFits<char16_t>(length));
    417  MOZ_ASSERT_IF(!base->hasTwoByteChars(),
    418                !JSInlineString::lengthFits<JS::Latin1Char>(length));
    419 
    420  // Invariant: if a tenured dependent string points to chars in the nursery,
    421  // then the string must be in the store buffer.
    422  //
    423  // Refuse to create a chain tenured -> tenured -> nursery (with nursery
    424  // chars). The same holds for anything else that might create length > 1
    425  // chains of dependent strings.
    426  bool mustContract;
    427  if constexpr (contract == JS::ContractBaseChain::Contract) {
    428    mustContract = true;
    429  } else {
    430    auto& nursery = cx->runtime()->gc.nursery();
    431    mustContract = nursery.isInside(base->nonInlineCharsRaw());
    432  }
    433 
    434  if (mustContract) {
    435    // Try to avoid long chains of dependent strings. We can't avoid these
    436    // entirely, however, due to how ropes are flattened.
    437    if (base->isDependent()) {
    438      start += base->asDependent().baseOffset();
    439      base = base->asDependent().base();
    440    }
    441  }
    442 
    443  MOZ_ASSERT(start + length <= base->length());
    444 
    445  JSDependentString* str;
    446  if constexpr (contract == JS::ContractBaseChain::Contract) {
    447    return cx->newCell<JSDependentString>(heap, base, start, length);
    448  }
    449 
    450  str = cx->newCell<JSDependentString>(heap, base, start, length);
    451  if (str && base->isDependent() && base->isTenured()) {
    452    // Tenured dependent -> nursery base string edges are problematic for
    453    // deduplication if the tenured dependent string can itself have strings
    454    // dependent on it. Whenever such a thing can be created, the nursery base
    455    // must be marked as non-deduplicatable.
    456    JSString* rootBase = base;
    457    while (rootBase->isDependent()) {
    458      rootBase = rootBase->base();
    459    }
    460    if (!rootBase->isTenured()) {
    461      rootBase->setNonDeduplicatable();
    462    }
    463  }
    464 
    465  return str;
    466 }
    467 
    468 /* static */
    469 inline JSLinearString* JSDependentString::new_(JSContext* cx,
    470                                               JSLinearString* base,
    471                                               size_t start, size_t length,
    472                                               js::gc::Heap heap) {
    473  return newImpl_<JS::ContractBaseChain::Contract>(cx, base, start, length,
    474                                                   heap);
    475 }
    476 
    477 inline JSLinearString::JSLinearString(const char16_t* chars, size_t length,
    478                                      bool hasBuffer) {
    479  uint32_t flags = INIT_LINEAR_FLAGS | (hasBuffer ? HAS_STRING_BUFFER_BIT : 0);
    480  setLengthAndFlags(length, flags);
    481  // Check that the new buffer is located in the StringBufferArena.
    482  checkStringCharsArena(chars, hasBuffer);
    483  d.s.u2.nonInlineCharsTwoByte = chars;
    484 }
    485 
    486 inline JSLinearString::JSLinearString(const JS::Latin1Char* chars,
    487                                      size_t length, bool hasBuffer) {
    488  uint32_t flags = INIT_LINEAR_FLAGS | LATIN1_CHARS_BIT |
    489                   (hasBuffer ? HAS_STRING_BUFFER_BIT : 0);
    490  setLengthAndFlags(length, flags);
    491  // Check that the new buffer is located in the StringBufferArena.
    492  checkStringCharsArena(chars, hasBuffer);
    493  d.s.u2.nonInlineCharsLatin1 = chars;
    494 }
    495 
    496 template <typename CharT>
    497 inline JSLinearString::JSLinearString(
    498    JS::MutableHandle<JSString::OwnedChars<CharT>> chars) {
    499  // Note that it is possible that the chars may have been moved from the
    500  // nursery to the malloc heap when allocating the Cell that this constructor
    501  // is initializing.
    502  MOZ_ASSERT(chars.data());
    503  checkStringCharsArena(chars.data(), chars.hasStringBuffer());
    504  if (isTenured()) {
    505    chars.ensureNonNursery();
    506  }
    507  uint32_t flags = INIT_LINEAR_FLAGS;
    508  if (chars.hasStringBuffer()) {
    509    flags |= HAS_STRING_BUFFER_BIT;
    510  }
    511  if constexpr (std::is_same_v<CharT, char16_t>) {
    512    setLengthAndFlags(chars.length(), flags);
    513    d.s.u2.nonInlineCharsTwoByte = chars.data();
    514  } else {
    515    setLengthAndFlags(chars.length(), flags | LATIN1_CHARS_BIT);
    516    d.s.u2.nonInlineCharsLatin1 = chars.data();
    517  }
    518 }
    519 
    520 void JSLinearString::disownCharsBecauseError() {
    521  setLengthAndFlags(0, INIT_LINEAR_FLAGS | LATIN1_CHARS_BIT);
    522  d.s.u2.nonInlineCharsLatin1 = nullptr;
    523 }
    524 
    525 template <js::AllowGC allowGC, typename CharT>
    526 MOZ_ALWAYS_INLINE JSLinearString* JSLinearString::new_(
    527    JSContext* cx, JS::MutableHandle<JSString::OwnedChars<CharT>> chars,
    528    js::gc::Heap heap) {
    529  if (MOZ_UNLIKELY(!validateLengthInternal<allowGC>(cx, chars.length()))) {
    530    return nullptr;
    531  }
    532 
    533  return newValidLength<allowGC>(cx, chars, heap);
    534 }
    535 
    536 template <js::AllowGC allowGC, typename CharT>
    537 MOZ_ALWAYS_INLINE JSLinearString* JSLinearString::newValidLength(
    538    JSContext* cx, JS::MutableHandle<JSString::OwnedChars<CharT>> chars,
    539    js::gc::Heap heap) {
    540  MOZ_ASSERT(!cx->zone()->isAtomsZone());
    541  MOZ_ASSERT(!JSInlineString::lengthFits<CharT>(chars.length()));
    542  JSLinearString* str = cx->newCell<JSLinearString, allowGC>(heap, chars);
    543  if (!str) {
    544    return nullptr;
    545  }
    546 
    547  if (!str->isTenured()) {
    548    // If the following registration fails, the string is partially initialized
    549    // and must be made valid, or its finalizer may attempt to free
    550    // uninitialized memory.
    551    bool ok = true;
    552    if (chars.isMalloced()) {
    553      ok = cx->nursery().registerMallocedBuffer(chars.data(), chars.size());
    554    } else if (chars.hasStringBuffer()) {
    555      ok = cx->nursery().addStringBuffer(str);
    556    }
    557    if (!ok) {
    558      str->disownCharsBecauseError();
    559      if (allowGC) {
    560        ReportOutOfMemory(cx);
    561      }
    562      return nullptr;
    563    }
    564  } else {
    565    // Note: this will overcount if the same StringBuffer is used by multiple JS
    566    // strings. Unfortunately we don't have a good way to avoid this.
    567    cx->zone()->addCellMemory(str, chars.size(), js::MemoryUse::StringContents);
    568  }
    569 
    570  // Either the tenured Cell or the nursery's registry owns the chars now.
    571  chars.release();
    572 
    573  return str;
    574 }
    575 
    576 template <typename CharT>
    577 MOZ_ALWAYS_INLINE JSAtom* JSAtom::newValidLength(JSContext* cx,
    578                                                 OwnedChars<CharT>& chars,
    579                                                 js::HashNumber hash) {
    580  size_t length = chars.length();
    581  MOZ_ASSERT(validateLength(cx, length));
    582  MOZ_ASSERT(cx->zone()->isAtomsZone());
    583 
    584  // Note: atom allocation can't GC. The unrooted |chars| argument relies on
    585  // this.
    586  JSAtom* str = cx->newCell<js::NormalAtom, js::NoGC>(chars, hash);
    587  if (!str) {
    588    return nullptr;
    589  }
    590 
    591  // The atom now owns the chars.
    592  chars.release();
    593 
    594  MOZ_ASSERT(str->isTenured());
    595  cx->zone()->addCellMemory(str, length * sizeof(CharT),
    596                            js::MemoryUse::StringContents);
    597 
    598  return str;
    599 }
    600 
    601 inline js::PropertyName* JSLinearString::toPropertyName(JSContext* cx) {
    602 #ifdef DEBUG
    603  uint32_t dummy;
    604  MOZ_ASSERT(!isIndex(&dummy));
    605 #endif
    606  JSAtom* atom = js::AtomizeString(cx, this);
    607  if (!atom) {
    608    return nullptr;
    609  }
    610  return atom->asPropertyName();
    611 }
    612 
    613 // String characters are movable in the following cases:
    614 //
    615 // 1. Inline nursery strings (moved during promotion)
    616 // 2. Nursery strings with nursery chars (moved during promotion)
    617 // 3. Nursery strings that are deduplicated (moved during promotion)
    618 // 4. Inline tenured strings (moved during compaction)
    619 //
    620 // This method does not consider #3, because if this method returns true and the
    621 // caller does not want the characters to move, it can fix them in place by
    622 // setting the nondeduplicatable bit. (If the bit were already taken into
    623 // consideration, then the caller wouldn't know whether the movability is
    624 // "fixable" or not. If it is *only* movable because of the lack of the bit
    625 // being set, then it is fixable by setting the bit.)
    626 bool JSLinearString::hasMovableChars() const {
    627  const JSLinearString* topBase = this;
    628  while (topBase->hasBase()) {
    629    topBase = topBase->base();
    630  }
    631  if (topBase->isInline()) {
    632    return true;
    633  }
    634  if (topBase->isTenured()) {
    635    return false;
    636  }
    637  return topBase->storeBuffer()->nursery().isInside(
    638      topBase->nonInlineCharsRaw());
    639 }
    640 
    641 template <js::AllowGC allowGC>
    642 MOZ_ALWAYS_INLINE JSThinInlineString* JSThinInlineString::new_(
    643    JSContext* cx, js::gc::Heap heap) {
    644  MOZ_ASSERT(!cx->zone()->isAtomsZone());
    645  return cx->newCell<JSThinInlineString, allowGC>(heap);
    646 }
    647 
    648 template <js::AllowGC allowGC>
    649 MOZ_ALWAYS_INLINE JSFatInlineString* JSFatInlineString::new_(
    650    JSContext* cx, js::gc::Heap heap) {
    651  MOZ_ASSERT(!cx->zone()->isAtomsZone());
    652  return cx->newCell<JSFatInlineString, allowGC>(heap);
    653 }
    654 
    655 inline JSThinInlineString::JSThinInlineString(size_t length,
    656                                              JS::Latin1Char** chars) {
    657  MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
    658  setLengthAndFlags(length, INIT_THIN_INLINE_FLAGS | LATIN1_CHARS_BIT);
    659  *chars = d.inlineStorageLatin1;
    660 }
    661 
    662 inline JSThinInlineString::JSThinInlineString(size_t length, char16_t** chars) {
    663  MOZ_ASSERT(lengthFits<char16_t>(length));
    664  setLengthAndFlags(length, INIT_THIN_INLINE_FLAGS);
    665  *chars = d.inlineStorageTwoByte;
    666 }
    667 
    668 inline JSFatInlineString::JSFatInlineString(size_t length,
    669                                            JS::Latin1Char** chars) {
    670  MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
    671  setLengthAndFlags(length, INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT);
    672  *chars = d.inlineStorageLatin1;
    673 }
    674 
    675 inline JSFatInlineString::JSFatInlineString(size_t length, char16_t** chars) {
    676  MOZ_ASSERT(lengthFits<char16_t>(length));
    677  setLengthAndFlags(length, INIT_FAT_INLINE_FLAGS);
    678  *chars = d.inlineStorageTwoByte;
    679 }
    680 
    681 inline JSExternalString::JSExternalString(
    682    const char16_t* chars, size_t length,
    683    const JSExternalStringCallbacks* callbacks) {
    684  MOZ_ASSERT(callbacks);
    685  setLengthAndFlags(length, EXTERNAL_FLAGS);
    686  d.s.u2.nonInlineCharsTwoByte = chars;
    687  d.s.u3.externalCallbacks = callbacks;
    688 }
    689 
    690 inline JSExternalString::JSExternalString(
    691    const JS::Latin1Char* chars, size_t length,
    692    const JSExternalStringCallbacks* callbacks) {
    693  MOZ_ASSERT(callbacks);
    694  setLengthAndFlags(length, EXTERNAL_FLAGS | LATIN1_CHARS_BIT);
    695  d.s.u2.nonInlineCharsLatin1 = chars;
    696  d.s.u3.externalCallbacks = callbacks;
    697 }
    698 
    699 template <typename CharT>
    700 /* static */
    701 MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::newImpl(
    702    JSContext* cx, const CharT* chars, size_t length,
    703    const JSExternalStringCallbacks* callbacks) {
    704  if (MOZ_UNLIKELY(!validateLength(cx, length))) {
    705    return nullptr;
    706  }
    707  auto* str = cx->newCell<JSExternalString>(chars, length, callbacks);
    708 
    709  if (!str) {
    710    return nullptr;
    711  }
    712  size_t nbytes = length * sizeof(CharT);
    713 
    714  MOZ_ASSERT(str->isTenured());
    715  js::AddCellMemory(str, nbytes, js::MemoryUse::StringContents);
    716 
    717  return str;
    718 }
    719 
    720 /* static */
    721 MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(
    722    JSContext* cx, const JS::Latin1Char* chars, size_t length,
    723    const JSExternalStringCallbacks* callbacks) {
    724  return newImpl(cx, chars, length, callbacks);
    725 }
    726 
    727 /* static */
    728 MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(
    729    JSContext* cx, const char16_t* chars, size_t length,
    730    const JSExternalStringCallbacks* callbacks) {
    731  return newImpl(cx, chars, length, callbacks);
    732 }
    733 
    734 template <typename CharT>
    735 inline js::NormalAtom::NormalAtom(const OwnedChars<CharT>& chars,
    736                                  js::HashNumber hash)
    737    : hash_(hash) {
    738  // Check that the new buffer is located in the StringBufferArena
    739  checkStringCharsArena(chars.data(), chars.hasStringBuffer());
    740 
    741  uint32_t flags = INIT_LINEAR_FLAGS | ATOM_BIT;
    742  if (chars.hasStringBuffer()) {
    743    flags |= HAS_STRING_BUFFER_BIT;
    744  }
    745 
    746  if constexpr (std::is_same_v<CharT, char16_t>) {
    747    setLengthAndFlags(chars.length(), flags);
    748    d.s.u2.nonInlineCharsTwoByte = chars.data();
    749  } else {
    750    setLengthAndFlags(chars.length(), flags | LATIN1_CHARS_BIT);
    751    d.s.u2.nonInlineCharsLatin1 = chars.data();
    752  }
    753 }
    754 
    755 #ifndef JS_64BIT
    756 inline js::ThinInlineAtom::ThinInlineAtom(size_t length, JS::Latin1Char** chars,
    757                                          js::HashNumber hash)
    758    : NormalAtom(hash) {
    759  setLengthAndFlags(length,
    760                    INIT_THIN_INLINE_FLAGS | LATIN1_CHARS_BIT | ATOM_BIT);
    761  *chars = d.inlineStorageLatin1;
    762 }
    763 
    764 inline js::ThinInlineAtom::ThinInlineAtom(size_t length, char16_t** chars,
    765                                          js::HashNumber hash)
    766    : NormalAtom(hash) {
    767  setLengthAndFlags(length, INIT_THIN_INLINE_FLAGS | ATOM_BIT);
    768  *chars = d.inlineStorageTwoByte;
    769 }
    770 #endif
    771 
    772 inline js::FatInlineAtom::FatInlineAtom(size_t length, JS::Latin1Char** chars,
    773                                        js::HashNumber hash)
    774    : hash_(hash) {
    775  MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
    776  setLengthAndFlags(length,
    777                    INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT | ATOM_BIT);
    778  *chars = d.inlineStorageLatin1;
    779 }
    780 
    781 inline js::FatInlineAtom::FatInlineAtom(size_t length, char16_t** chars,
    782                                        js::HashNumber hash)
    783    : hash_(hash) {
    784  MOZ_ASSERT(lengthFits<char16_t>(length));
    785  setLengthAndFlags(length, INIT_FAT_INLINE_FLAGS | ATOM_BIT);
    786  *chars = d.inlineStorageTwoByte;
    787 }
    788 
    789 inline JSLinearString* js::StaticStrings::getUnitString(JSContext* cx,
    790                                                        char16_t c) {
    791  if (c < UNIT_STATIC_LIMIT) {
    792    return getUnit(c);
    793  }
    794  return js::NewInlineString<CanGC>(cx, {c}, 1);
    795 }
    796 
    797 inline JSLinearString* js::StaticStrings::getUnitStringForElement(
    798    JSContext* cx, JSString* str, size_t index) {
    799  MOZ_ASSERT(index < str->length());
    800 
    801  char16_t c;
    802  if (!str->getChar(cx, index, &c)) {
    803    return nullptr;
    804  }
    805  return getUnitString(cx, c);
    806 }
    807 
    808 inline JSLinearString* js::StaticStrings::getUnitStringForElement(
    809    JSContext* cx, const JSLinearString* str, size_t index) {
    810  MOZ_ASSERT(index < str->length());
    811 
    812  char16_t c = str->latin1OrTwoByteChar(index);
    813  return getUnitString(cx, c);
    814 }
    815 
    816 MOZ_ALWAYS_INLINE void JSString::finalize(JS::GCContext* gcx) {
    817  /* FatInline strings are in a different arena. */
    818  MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
    819  MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
    820 
    821  if (isLinear()) {
    822    asLinear().finalize(gcx);
    823  } else {
    824    MOZ_ASSERT(isRope());
    825  }
    826 }
    827 
    828 inline void JSLinearString::finalize(JS::GCContext* gcx) {
    829  MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
    830  MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
    831 
    832  if (!isInline() && !isDependent()) {
    833    size_t size = allocSize();
    834    if (hasStringBuffer()) {
    835      mozilla::StringBuffer* buffer = stringBuffer();
    836      buffer->Release();
    837      gcx->removeCellMemory(this, size, js::MemoryUse::StringContents);
    838    } else {
    839      gcx->free_(this, nonInlineCharsRaw(), size,
    840                 js::MemoryUse::StringContents);
    841    }
    842  }
    843 }
    844 
    845 inline void JSFatInlineString::finalize(JS::GCContext* gcx) {
    846  MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_STRING);
    847  MOZ_ASSERT(isInline());
    848 
    849  // Nothing to do.
    850 }
    851 
    852 inline void js::FatInlineAtom::finalize(JS::GCContext* gcx) {
    853  MOZ_ASSERT(JSString::isAtom());
    854  MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_ATOM);
    855 
    856  // Nothing to do.
    857 }
    858 
    859 inline void JSExternalString::finalize(JS::GCContext* gcx) {
    860  MOZ_ASSERT(JSString::isExternal());
    861 
    862  if (hasLatin1Chars()) {
    863    size_t nbytes = length() * sizeof(JS::Latin1Char);
    864    gcx->removeCellMemory(this, nbytes, js::MemoryUse::StringContents);
    865 
    866    callbacks()->finalize(const_cast<JS::Latin1Char*>(rawLatin1Chars()));
    867  } else {
    868    size_t nbytes = length() * sizeof(char16_t);
    869    gcx->removeCellMemory(this, nbytes, js::MemoryUse::StringContents);
    870 
    871    callbacks()->finalize(const_cast<char16_t*>(rawTwoByteChars()));
    872  }
    873 }
    874 
    875 #endif /* vm_StringType_inl_h */