tor-browser

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

StringType.cpp (108300B)


      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 #include "vm/StringType-inl.h"
      8 
      9 #include "mozilla/DebugOnly.h"
     10 #include "mozilla/HashFunctions.h"
     11 #include "mozilla/Latin1.h"
     12 #include "mozilla/MemoryReporting.h"
     13 #include "mozilla/PodOperations.h"
     14 #include "mozilla/RangedPtr.h"
     15 #include "mozilla/StringBuffer.h"
     16 #include "mozilla/TextUtils.h"
     17 #include "mozilla/Utf8.h"
     18 #include "mozilla/Vector.h"
     19 
     20 #include <algorithm>    // std::{all_of,copy_n,enable_if,is_const,move}
     21 #include <type_traits>  // std::is_same, std::is_unsigned
     22 
     23 #include "jsfriendapi.h"
     24 #include "jsnum.h"
     25 
     26 #include "builtin/Boolean.h"
     27 #include "gc/AllocKind.h"
     28 #include "gc/MaybeRooted.h"
     29 #include "gc/Nursery.h"
     30 #include "js/CharacterEncoding.h"
     31 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     32 #include "js/Printer.h"               // js::GenericPrinter
     33 #include "js/PropertyAndElement.h"    // JS_DefineElement
     34 #include "js/SourceText.h"            // JS::SourceText
     35 #include "js/StableStringChars.h"
     36 #include "js/UbiNode.h"
     37 #include "util/Identifier.h"  // js::IsIdentifierNameOrPrivateName
     38 #include "util/Unicode.h"
     39 #include "vm/GeckoProfiler.h"
     40 #include "vm/JSONPrinter.h"  // js::JSONPrinter
     41 #include "vm/StaticStrings.h"
     42 #include "vm/ToSource.h"  // js::ValueToSource
     43 
     44 #include "gc/Marking-inl.h"
     45 #include "vm/GeckoProfiler-inl.h"
     46 
     47 using namespace js;
     48 
     49 using mozilla::AsWritableChars;
     50 using mozilla::ConvertLatin1toUtf16;
     51 using mozilla::IsAsciiDigit;
     52 using mozilla::IsUtf16Latin1;
     53 using mozilla::LossyConvertUtf16toLatin1;
     54 using mozilla::PodCopy;
     55 using mozilla::RangedPtr;
     56 using mozilla::RoundUpPow2;
     57 using mozilla::Span;
     58 
     59 using JS::AutoCheckCannotGC;
     60 using JS::AutoStableStringChars;
     61 
     62 using UniqueLatin1Chars = UniquePtr<Latin1Char[], JS::FreePolicy>;
     63 
     64 size_t JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
     65  // JSRope: do nothing, we'll count all children chars when we hit the leaf
     66  // strings.
     67  if (isRope()) {
     68    return 0;
     69  }
     70 
     71  MOZ_ASSERT(isLinear());
     72 
     73  // JSDependentString: do nothing, we'll count the chars when we hit the base
     74  // string.
     75  if (isDependent()) {
     76    return 0;
     77  }
     78 
     79  // JSExternalString: Ask the embedding to tell us what's going on.
     80  if (isExternal()) {
     81    // Our callback isn't supposed to cause GC.
     82    JS::AutoSuppressGCAnalysis nogc;
     83    JSExternalString& external = asExternal();
     84    if (external.hasLatin1Chars()) {
     85      return asExternal().callbacks()->sizeOfBuffer(external.latin1Chars(),
     86                                                    mallocSizeOf);
     87    } else {
     88      return asExternal().callbacks()->sizeOfBuffer(external.twoByteChars(),
     89                                                    mallocSizeOf);
     90    }
     91  }
     92 
     93  // JSInlineString, JSFatInlineString, js::ThinInlineAtom, js::FatInlineAtom:
     94  // the chars are inline.
     95  if (isInline()) {
     96    return 0;
     97  }
     98 
     99  JSLinearString& linear = asLinear();
    100 
    101  if (hasStringBuffer()) {
    102    return linear.stringBuffer()->SizeOfIncludingThisIfUnshared(mallocSizeOf);
    103  }
    104 
    105  // Chars in the nursery are owned by the nursery.
    106  if (!ownsMallocedChars()) {
    107    return 0;
    108  }
    109 
    110  // Everything else: measure the space for the chars.
    111  return linear.hasLatin1Chars() ? mallocSizeOf(linear.rawLatin1Chars())
    112                                 : mallocSizeOf(linear.rawTwoByteChars());
    113 }
    114 
    115 JS::ubi::Node::Size JS::ubi::Concrete<JSString>::size(
    116    mozilla::MallocSizeOf mallocSizeOf) const {
    117  JSString& str = get();
    118  size_t size;
    119  if (str.isAtom()) {
    120    if (str.isInline()) {
    121      size = str.isFatInline() ? sizeof(js::FatInlineAtom)
    122                               : sizeof(js::ThinInlineAtom);
    123    } else {
    124      size = sizeof(js::NormalAtom);
    125    }
    126  } else {
    127    size = str.isFatInline() ? sizeof(JSFatInlineString) : sizeof(JSString);
    128  }
    129 
    130  if (IsInsideNursery(&str)) {
    131    size += Nursery::nurseryCellHeaderSize();
    132  }
    133 
    134  size += str.sizeOfExcludingThis(mallocSizeOf);
    135 
    136  return size;
    137 }
    138 
    139 const char16_t JS::ubi::Concrete<JSString>::concreteTypeName[] = u"JSString";
    140 
    141 mozilla::Maybe<std::tuple<size_t, size_t>> JSString::encodeUTF8Partial(
    142    const JS::AutoRequireNoGC& nogc, mozilla::Span<char> buffer) const {
    143  mozilla::Vector<const JSString*, 16, SystemAllocPolicy> stack;
    144  const JSString* current = this;
    145  char16_t pendingLeadSurrogate = 0;  // U+0000 means no pending lead surrogate
    146  size_t totalRead = 0;
    147  size_t totalWritten = 0;
    148  for (;;) {
    149    if (current->isRope()) {
    150      JSRope& rope = current->asRope();
    151      if (!stack.append(rope.rightChild())) {
    152        // OOM
    153        return mozilla::Nothing();
    154      }
    155      current = rope.leftChild();
    156      continue;
    157    }
    158 
    159    JSLinearString& linear = current->asLinear();
    160    if (MOZ_LIKELY(linear.hasLatin1Chars())) {
    161      if (MOZ_UNLIKELY(pendingLeadSurrogate)) {
    162        if (buffer.Length() < 3) {
    163          return mozilla::Some(std::make_tuple(totalRead, totalWritten));
    164        }
    165        buffer[0] = '\xEF';
    166        buffer[1] = '\xBF';
    167        buffer[2] = '\xBD';
    168        buffer = buffer.From(3);
    169        totalRead += 1;  // pendingLeadSurrogate
    170        totalWritten += 3;
    171        pendingLeadSurrogate = 0;
    172      }
    173      auto src = mozilla::AsChars(
    174          mozilla::Span(linear.latin1Chars(nogc), linear.length()));
    175      size_t read;
    176      size_t written;
    177      std::tie(read, written) =
    178          mozilla::ConvertLatin1toUtf8Partial(src, buffer);
    179      buffer = buffer.From(written);
    180      totalRead += read;
    181      totalWritten += written;
    182      if (read < src.Length()) {
    183        return mozilla::Some(std::make_tuple(totalRead, totalWritten));
    184      }
    185    } else {
    186      auto src = mozilla::Span(linear.twoByteChars(nogc), linear.length());
    187      if (MOZ_UNLIKELY(pendingLeadSurrogate)) {
    188        char16_t first = 0;
    189        if (!src.IsEmpty()) {
    190          first = src[0];
    191        }
    192        if (unicode::IsTrailSurrogate(first)) {
    193          // Got a surrogate pair
    194          if (buffer.Length() < 4) {
    195            return mozilla::Some(std::make_tuple(totalRead, totalWritten));
    196          }
    197          uint32_t astral = unicode::UTF16Decode(pendingLeadSurrogate, first);
    198          buffer[0] = char(0b1111'0000 | (astral >> 18));
    199          buffer[1] = char(0b1000'0000 | ((astral >> 12) & 0b11'1111));
    200          buffer[2] = char(0b1000'0000 | ((astral >> 6) & 0b11'1111));
    201          buffer[3] = char(0b1000'0000 | (astral & 0b11'1111));
    202          src = src.From(1);
    203          buffer = buffer.From(4);
    204          totalRead += 2;  // both pendingLeadSurrogate and first!
    205          totalWritten += 4;
    206        } else {
    207          // unpaired surrogate
    208          if (buffer.Length() < 3) {
    209            return mozilla::Some(std::make_tuple(totalRead, totalWritten));
    210          }
    211          buffer[0] = '\xEF';
    212          buffer[1] = '\xBF';
    213          buffer[2] = '\xBD';
    214          buffer = buffer.From(3);
    215          totalRead += 1;  // pendingLeadSurrogate
    216          totalWritten += 3;
    217        }
    218        pendingLeadSurrogate = 0;
    219      }
    220      if (!src.IsEmpty()) {
    221        char16_t last = src[src.Length() - 1];
    222        if (unicode::IsLeadSurrogate(last)) {
    223          src = src.To(src.Length() - 1);
    224          pendingLeadSurrogate = last;
    225        } else {
    226          MOZ_ASSERT(!pendingLeadSurrogate);
    227        }
    228        size_t read;
    229        size_t written;
    230        std::tie(read, written) =
    231            mozilla::ConvertUtf16toUtf8Partial(src, buffer);
    232        buffer = buffer.From(written);
    233        totalRead += read;
    234        totalWritten += written;
    235        if (read < src.Length()) {
    236          return mozilla::Some(std::make_tuple(totalRead, totalWritten));
    237        }
    238      }
    239    }
    240    if (stack.empty()) {
    241      break;
    242    }
    243    current = stack.popCopy();
    244  }
    245  if (MOZ_UNLIKELY(pendingLeadSurrogate)) {
    246    if (buffer.Length() < 3) {
    247      return mozilla::Some(std::make_tuple(totalRead, totalWritten));
    248    }
    249    buffer[0] = '\xEF';
    250    buffer[1] = '\xBF';
    251    buffer[2] = '\xBD';
    252    // No need to update buffer and pendingLeadSurrogate anymore
    253    totalRead += 1;
    254    totalWritten += 3;
    255  }
    256  return mozilla::Some(std::make_tuple(totalRead, totalWritten));
    257 }
    258 
    259 #if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)
    260 template <typename CharT>
    261 /*static */
    262 void JSString::dumpCharsNoQuote(const CharT* s, size_t n,
    263                                js::GenericPrinter& out) {
    264  for (size_t i = 0; i < n; i++) {
    265    char16_t c = s[i];
    266    if (c == '"') {
    267      out.put("\\\"");
    268    } else if (c == '\'') {
    269      out.put("\\'");
    270    } else if (c == '`') {
    271      out.put("\\`");
    272    } else if (c == '\\') {
    273      out.put("\\\\");
    274    } else if (c == '\r') {
    275      out.put("\\r");
    276    } else if (c == '\n') {
    277      out.put("\\n");
    278    } else if (c == '\t') {
    279      out.put("\\t");
    280    } else if (c >= 32 && c < 127) {
    281      out.putChar((char)s[i]);
    282    } else if (c <= 255) {
    283      out.printf("\\x%02x", unsigned(c));
    284    } else {
    285      out.printf("\\u%04x", unsigned(c));
    286    }
    287  }
    288 }
    289 
    290 /* static */
    291 template void JSString::dumpCharsNoQuote(const Latin1Char* s, size_t n,
    292                                         js::GenericPrinter& out);
    293 
    294 /* static */
    295 template void JSString::dumpCharsNoQuote(const char16_t* s, size_t n,
    296                                         js::GenericPrinter& out);
    297 
    298 void JSString::dump() const {
    299  js::Fprinter out(stderr);
    300  dump(out);
    301 }
    302 
    303 void JSString::dump(js::GenericPrinter& out) const {
    304  js::JSONPrinter json(out);
    305  dump(json);
    306  out.put("\n");
    307 }
    308 
    309 void JSString::dump(js::JSONPrinter& json) const {
    310  json.beginObject();
    311  dumpFields(json);
    312  json.endObject();
    313 }
    314 
    315 const char* RepresentationToString(const JSString* s) {
    316  if (s->isAtom()) {
    317    return "JSAtom";
    318  }
    319 
    320  if (s->isLinear()) {
    321    if (s->isDependent()) {
    322      return "JSDependentString";
    323    }
    324    if (s->isExternal()) {
    325      return "JSExternalString";
    326    }
    327    if (s->isExtensible()) {
    328      return "JSExtensibleString";
    329    }
    330 
    331    if (s->isInline()) {
    332      if (s->isFatInline()) {
    333        return "JSFatInlineString";
    334      }
    335      return "JSThinInlineString";
    336    }
    337 
    338    return "JSLinearString";
    339  }
    340 
    341  if (s->isRope()) {
    342    return "JSRope";
    343  }
    344 
    345  return "JSString";
    346 }
    347 
    348 template <typename KnownF, typename UnknownF>
    349 void ForEachStringFlag(const JSString* str, uint32_t flags, KnownF known,
    350                       UnknownF unknown) {
    351  for (uint32_t i = js::Bit(3); i < js::Bit(17); i = i << 1) {
    352    if (!(flags & i)) {
    353      continue;
    354    }
    355    switch (i) {
    356      case JSString::ATOM_BIT:
    357        known("ATOM_BIT");
    358        break;
    359      case JSString::LINEAR_BIT:
    360        known("LINEAR_BIT");
    361        break;
    362      case JSString::DEPENDENT_BIT:
    363        known("DEPENDENT_BIT");
    364        break;
    365      case JSString::INLINE_CHARS_BIT:
    366        known("INLINE_BIT");
    367        break;
    368      case JSString::LINEAR_IS_EXTENSIBLE_BIT:
    369        static_assert(JSString::LINEAR_IS_EXTENSIBLE_BIT ==
    370                      JSString::INLINE_IS_FAT_BIT);
    371        if (str->isLinear()) {
    372          if (str->isInline()) {
    373            known("FAT");
    374          } else if (!str->isAtom()) {
    375            known("EXTENSIBLE");
    376          } else {
    377            unknown(i);
    378          }
    379        } else {
    380          unknown(i);
    381        }
    382        break;
    383      case JSString::LINEAR_IS_EXTERNAL_BIT:
    384        static_assert(JSString::LINEAR_IS_EXTERNAL_BIT ==
    385                      JSString::ATOM_IS_PERMANENT_BIT);
    386        if (str->isAtom()) {
    387          known("PERMANENT");
    388        } else if (str->isLinear()) {
    389          known("EXTERNAL");
    390        } else {
    391          unknown(i);
    392        }
    393        break;
    394      case JSString::LATIN1_CHARS_BIT:
    395        known("LATIN1_CHARS_BIT");
    396        break;
    397      case JSString::HAS_STRING_BUFFER_BIT:
    398        known("HAS_STRING_BUFFER_BIT");
    399        break;
    400      case JSString::ATOM_IS_INDEX_BIT:
    401        if (str->isAtom()) {
    402          known("ATOM_IS_INDEX_BIT");
    403        } else {
    404          known("ATOM_REF_BIT");
    405        }
    406        break;
    407      case JSString::INDEX_VALUE_BIT:
    408        known("INDEX_VALUE_BIT");
    409        break;
    410      case JSString::IN_STRING_TO_ATOM_CACHE:
    411        known("IN_STRING_TO_ATOM_CACHE");
    412        break;
    413      case JSString::FLATTEN_VISIT_RIGHT:
    414        if (str->isRope()) {
    415          known("FLATTEN_VISIT_RIGHT");
    416        } else {
    417          known("DEPENDED_ON_BIT");
    418        }
    419        break;
    420      case JSString::FLATTEN_FINISH_NODE:
    421        static_assert(JSString::FLATTEN_FINISH_NODE ==
    422                      JSString::PINNED_ATOM_BIT);
    423        if (str->isRope()) {
    424          known("FLATTEN_FINISH_NODE");
    425        } else if (str->isAtom()) {
    426          known("PINNED_ATOM_BIT");
    427        } else {
    428          known("NON_DEDUP_BIT");
    429        }
    430        break;
    431      default:
    432        unknown(i);
    433        break;
    434    }
    435  }
    436 }
    437 
    438 void JSString::dumpFields(js::JSONPrinter& json) const {
    439  dumpCommonFields(json);
    440  dumpCharsFields(json);
    441 }
    442 
    443 void JSString::dumpCommonFields(js::JSONPrinter& json) const {
    444  json.formatProperty("address", "(%s*)0x%p", RepresentationToString(this),
    445                      this);
    446 
    447  json.beginInlineListProperty("flags");
    448  ForEachStringFlag(
    449      this, flags(), [&](const char* name) { json.value("%s", name); },
    450      [&](uint32_t value) { json.value("Unknown(%08x)", value); });
    451  json.endInlineList();
    452 
    453  if (hasIndexValue()) {
    454    json.property("indexValue", getIndexValue());
    455  }
    456 
    457  json.boolProperty("isTenured", isTenured());
    458 
    459  json.property("length", length());
    460 }
    461 
    462 void JSString::dumpCharsFields(js::JSONPrinter& json) const {
    463  if (isLinear()) {
    464    const JSLinearString* linear = &asLinear();
    465 
    466    AutoCheckCannotGC nogc;
    467    if (hasLatin1Chars()) {
    468      const Latin1Char* chars = linear->latin1Chars(nogc);
    469 
    470      json.formatProperty("chars", "(JS::Latin1Char*)0x%p", chars);
    471 
    472      js::GenericPrinter& out = json.beginStringProperty("value");
    473      dumpCharsNoQuote(chars, length(), out);
    474      json.endStringProperty();
    475    } else {
    476      const char16_t* chars = linear->twoByteChars(nogc);
    477 
    478      json.formatProperty("chars", "(char16_t*)0x%p", chars);
    479 
    480      js::GenericPrinter& out = json.beginStringProperty("value");
    481      dumpCharsNoQuote(chars, length(), out);
    482      json.endStringProperty();
    483    }
    484  } else {
    485    js::GenericPrinter& out = json.beginStringProperty("value");
    486    dumpCharsNoQuote(out);
    487    json.endStringProperty();
    488  }
    489 }
    490 
    491 void JSString::dumpRepresentation() const {
    492  js::Fprinter out(stderr);
    493  dumpRepresentation(out);
    494 }
    495 
    496 void JSString::dumpRepresentation(js::GenericPrinter& out) const {
    497  js::JSONPrinter json(out);
    498  dumpRepresentation(json);
    499  out.put("\n");
    500 }
    501 
    502 void JSString::dumpRepresentation(js::JSONPrinter& json) const {
    503  json.beginObject();
    504  dumpRepresentationFields(json);
    505  json.endObject();
    506 }
    507 
    508 void JSString::dumpRepresentationFields(js::JSONPrinter& json) const {
    509  dumpCommonFields(json);
    510 
    511  if (isAtom()) {
    512    asAtom().dumpOwnRepresentationFields(json);
    513  } else if (isLinear()) {
    514    asLinear().dumpOwnRepresentationFields(json);
    515 
    516    if (isDependent()) {
    517      asDependent().dumpOwnRepresentationFields(json);
    518    } else if (isExternal()) {
    519      asExternal().dumpOwnRepresentationFields(json);
    520    } else if (isExtensible()) {
    521      asExtensible().dumpOwnRepresentationFields(json);
    522    } else if (isInline()) {
    523      asInline().dumpOwnRepresentationFields(json);
    524    }
    525  } else if (isRope()) {
    526    asRope().dumpOwnRepresentationFields(json);
    527    // Rope already shows the chars.
    528    return;
    529  }
    530 
    531  dumpCharsFields(json);
    532 }
    533 
    534 void JSString::dumpStringContent(js::GenericPrinter& out) const {
    535  dumpCharsSingleQuote(out);
    536 
    537  out.printf(" @ (%s*)0x%p", RepresentationToString(this), this);
    538 }
    539 
    540 void JSString::dumpPropertyName(js::GenericPrinter& out) const {
    541  dumpCharsNoQuote(out);
    542 }
    543 
    544 void JSString::dumpChars(js::GenericPrinter& out) const {
    545  out.putChar('"');
    546  dumpCharsNoQuote(out);
    547  out.putChar('"');
    548 }
    549 
    550 void JSString::dumpCharsSingleQuote(js::GenericPrinter& out) const {
    551  out.putChar('\'');
    552  dumpCharsNoQuote(out);
    553  out.putChar('\'');
    554 }
    555 
    556 void JSString::dumpCharsNoQuote(js::GenericPrinter& out) const {
    557  if (isLinear()) {
    558    const JSLinearString* linear = &asLinear();
    559 
    560    AutoCheckCannotGC nogc;
    561    if (hasLatin1Chars()) {
    562      dumpCharsNoQuote(linear->latin1Chars(nogc), length(), out);
    563    } else {
    564      dumpCharsNoQuote(linear->twoByteChars(nogc), length(), out);
    565    }
    566  } else if (isRope()) {
    567    JSRope* rope = &asRope();
    568    rope->leftChild()->dumpCharsNoQuote(out);
    569    rope->rightChild()->dumpCharsNoQuote(out);
    570  }
    571 }
    572 
    573 bool JSString::equals(const char* s) {
    574  JSLinearString* linear = ensureLinear(nullptr);
    575  if (!linear) {
    576    // This is DEBUG-only code.
    577    fprintf(stderr, "OOM in JSString::equals!\n");
    578    return false;
    579  }
    580 
    581  return StringEqualsAscii(linear, s);
    582 }
    583 #endif /* defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW) */
    584 
    585 JSExtensibleString& JSLinearString::makeExtensible(size_t capacity) {
    586  MOZ_ASSERT(!isDependent());
    587  MOZ_ASSERT(!isInline());
    588  MOZ_ASSERT(!isAtom());
    589  MOZ_ASSERT(!isExternal());
    590  MOZ_ASSERT(capacity >= length());
    591  size_t oldSize = allocSize();
    592  js::RemoveCellMemory(this, oldSize, js::MemoryUse::StringContents);
    593  setLengthAndFlags(length(), flags() | EXTENSIBLE_FLAGS);
    594  d.s.u3.capacity = capacity;
    595  size_t newSize = allocSize();
    596  js::AddCellMemory(this, newSize, js::MemoryUse::StringContents);
    597  MOZ_ASSERT(newSize >= oldSize);
    598  if (!isTenured() && newSize > oldSize) {
    599    auto& nursery = runtimeFromMainThread()->gc.nursery();
    600    nursery.addMallocedBufferBytes(newSize - oldSize);
    601  }
    602  return asExtensible();
    603 }
    604 
    605 template <typename CharT>
    606 static MOZ_ALWAYS_INLINE bool AllocCharsForFlatten(Nursery& nursery,
    607                                                   JSString* str, size_t length,
    608                                                   CharT** chars,
    609                                                   size_t* capacity,
    610                                                   bool* hasStringBuffer) {
    611  /*
    612   * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
    613   * next power of 2. This is similar to what we do with object elements; see
    614   * NativeObject::goodElementsAllocationAmount.
    615   */
    616  auto calcCapacity = [](size_t length, size_t maxCapacity) {
    617    static const size_t DOUBLING_MAX = 1024 * 1024;
    618    if (length > DOUBLING_MAX) {
    619      return std::min<size_t>(maxCapacity, length + (length / 8));
    620    }
    621    size_t capacity = RoundUpPow2(length);
    622    MOZ_ASSERT(capacity <= maxCapacity);
    623    return capacity;
    624  };
    625 
    626  if (length < JSString::MIN_BYTES_FOR_BUFFER / sizeof(CharT)) {
    627    *capacity = calcCapacity(length, JSString::MAX_LENGTH);
    628    MOZ_ASSERT(length <= *capacity);
    629    MOZ_ASSERT(*capacity <= JSString::MAX_LENGTH);
    630 
    631    size_t allocSize = *capacity * sizeof(CharT);
    632    void* buffer = nursery.allocNurseryOrMallocBuffer(
    633        str->zone(), str, allocSize, js::StringBufferArena);
    634    if (!buffer) {
    635      return false;
    636    }
    637 
    638    *chars = static_cast<CharT*>(buffer);
    639    *hasStringBuffer = false;
    640    return true;
    641  }
    642 
    643  using mozilla::StringBuffer;
    644 
    645  static_assert(StringBuffer::IsValidLength<CharT>(JSString::MAX_LENGTH),
    646                "JSString length must be valid for StringBuffer");
    647 
    648  // Include extra space for the header and the null-terminator before
    649  // calculating the capacity. This ensures we make good use of jemalloc's
    650  // bucket sizes. For example, for a Latin1 string with length 2000 we want to
    651  // get a capacity of 2039 (chars). With the StringBuffer header (8 bytes) and
    652  // the null-terminator this results in an allocation of 2048 bytes.
    653  //
    654  // Note: the null-terminator will not be included in the extensible string's
    655  // capacity field.
    656  static_assert(sizeof(StringBuffer) % sizeof(CharT) == 0);
    657  static constexpr size_t ExtraChars = sizeof(StringBuffer) / sizeof(CharT) + 1;
    658 
    659  size_t fullCapacity =
    660      calcCapacity(length + ExtraChars, JSString::MAX_LENGTH + ExtraChars);
    661  *capacity = fullCapacity - ExtraChars;
    662  MOZ_ASSERT(length <= *capacity);
    663  MOZ_ASSERT(*capacity <= JSString::MAX_LENGTH);
    664 
    665  RefPtr<StringBuffer> buffer = StringBuffer::Alloc(
    666      (*capacity + 1) * sizeof(CharT), mozilla::Some(js::StringBufferArena));
    667  if (!buffer) {
    668    return false;
    669  }
    670  if (!str->isTenured()) {
    671    auto* linear = static_cast<JSLinearString*>(str);  // True when we're done.
    672    if (!nursery.addExtensibleStringBuffer(linear, buffer)) {
    673      return false;
    674    }
    675  }
    676  // Transfer ownership to the caller, where the buffer will be used for the
    677  // extensible string.
    678  // Note: the null-terminator will be stored in flattenInternal.
    679  StringBuffer* buf;
    680  buffer.forget(&buf);
    681  *chars = static_cast<CharT*>(buf->Data());
    682  *hasStringBuffer = true;
    683  return true;
    684 }
    685 
    686 UniqueLatin1Chars JSRope::copyLatin1Chars(JSContext* maybecx,
    687                                          arena_id_t destArenaId) const {
    688  return copyCharsInternal<Latin1Char>(maybecx, destArenaId);
    689 }
    690 
    691 UniqueTwoByteChars JSRope::copyTwoByteChars(JSContext* maybecx,
    692                                            arena_id_t destArenaId) const {
    693  return copyCharsInternal<char16_t>(maybecx, destArenaId);
    694 }
    695 
    696 // Allocate chars for a string. If parameters and conditions allow, this will
    697 // try to allocate in the nursery, but this may always fall back to a malloc
    698 // allocation. The return value will record where the allocation happened.
    699 template <typename CharT>
    700 static MOZ_ALWAYS_INLINE JSString::OwnedChars<CharT> AllocChars(JSContext* cx,
    701                                                                size_t length,
    702                                                                gc::Heap heap) {
    703  if (heap == gc::Heap::Default && cx->zone()->allocNurseryStrings()) {
    704    MOZ_ASSERT(cx->nursery().isEnabled());
    705    void* buffer = cx->nursery().tryAllocateNurseryBuffer(
    706        cx->zone(), length * sizeof(CharT), js::StringBufferArena);
    707    if (buffer) {
    708      using Kind = typename JSString::OwnedChars<CharT>::Kind;
    709      return {static_cast<CharT*>(buffer), length, Kind::Nursery};
    710    }
    711  }
    712 
    713  static_assert(JSString::MIN_BYTES_FOR_BUFFER % sizeof(CharT) == 0);
    714 
    715  if (length < JSString::MIN_BYTES_FOR_BUFFER / sizeof(CharT)) {
    716    auto buffer =
    717        cx->make_pod_arena_array<CharT>(js::StringBufferArena, length);
    718    if (!buffer) {
    719      return {};
    720    }
    721    return {std::move(buffer), length};
    722  }
    723 
    724  if (MOZ_UNLIKELY(!mozilla::StringBuffer::IsValidLength<CharT>(length))) {
    725    ReportOversizedAllocation(cx, JSMSG_ALLOC_OVERFLOW);
    726    return {};
    727  }
    728 
    729  // Note: StringBuffers must be null-terminated.
    730  RefPtr<mozilla::StringBuffer> buffer = mozilla::StringBuffer::Alloc(
    731      (length + 1) * sizeof(CharT), mozilla::Some(js::StringBufferArena));
    732  if (!buffer) {
    733    ReportOutOfMemory(cx);
    734    return {};
    735  }
    736  static_cast<CharT*>(buffer->Data())[length] = '\0';
    737  return {std::move(buffer), length};
    738 }
    739 
    740 // Like AllocChars but for atom characters. Does not report an exception on OOM.
    741 template <typename CharT>
    742 JSString::OwnedChars<CharT> js::AllocAtomCharsValidLength(JSContext* cx,
    743                                                          size_t length) {
    744  MOZ_ASSERT(cx->zone()->isAtomsZone());
    745  MOZ_ASSERT(JSAtom::validateLength(cx, length));
    746  MOZ_ASSERT(mozilla::StringBuffer::IsValidLength<CharT>(length));
    747 
    748  static_assert(JSString::MIN_BYTES_FOR_BUFFER % sizeof(CharT) == 0);
    749 
    750  if (length < JSString::MIN_BYTES_FOR_BUFFER / sizeof(CharT)) {
    751    auto buffer =
    752        cx->make_pod_arena_array<CharT>(js::StringBufferArena, length);
    753    if (!buffer) {
    754      cx->recoverFromOutOfMemory();
    755      return {};
    756    }
    757    return {std::move(buffer), length};
    758  }
    759 
    760  // Note: StringBuffers must be null-terminated.
    761  RefPtr<mozilla::StringBuffer> buffer = mozilla::StringBuffer::Alloc(
    762      (length + 1) * sizeof(CharT), mozilla::Some(js::StringBufferArena));
    763  if (!buffer) {
    764    return {};
    765  }
    766  static_cast<CharT*>(buffer->Data())[length] = '\0';
    767  return {std::move(buffer), length};
    768 }
    769 
    770 template JSString::OwnedChars<Latin1Char> js::AllocAtomCharsValidLength(
    771    JSContext* cx, size_t length);
    772 template JSString::OwnedChars<char16_t> js::AllocAtomCharsValidLength(
    773    JSContext* cx, size_t length);
    774 
    775 template <typename CharT>
    776 UniquePtr<CharT[], JS::FreePolicy> JSRope::copyCharsInternal(
    777    JSContext* maybecx, arena_id_t destArenaId) const {
    778  // Left-leaning ropes are far more common than right-leaning ropes, so
    779  // perform a non-destructive traversal of the rope, right node first,
    780  // splatting each node's characters into a contiguous buffer.
    781 
    782  size_t n = length();
    783 
    784  UniquePtr<CharT[], JS::FreePolicy> out;
    785  if (maybecx) {
    786    out.reset(maybecx->pod_arena_malloc<CharT>(destArenaId, n));
    787  } else {
    788    out.reset(js_pod_arena_malloc<CharT>(destArenaId, n));
    789  }
    790 
    791  if (!out) {
    792    return nullptr;
    793  }
    794 
    795  Vector<const JSString*, 8, SystemAllocPolicy> nodeStack;
    796  const JSString* str = this;
    797  CharT* end = out.get() + str->length();
    798  while (true) {
    799    if (str->isRope()) {
    800      if (!nodeStack.append(str->asRope().leftChild())) {
    801        if (maybecx) {
    802          ReportOutOfMemory(maybecx);
    803        }
    804        return nullptr;
    805      }
    806      str = str->asRope().rightChild();
    807    } else {
    808      end -= str->length();
    809      CopyChars(end, str->asLinear());
    810      if (nodeStack.empty()) {
    811        break;
    812      }
    813      str = nodeStack.popCopy();
    814    }
    815  }
    816  MOZ_ASSERT(end == out.get());
    817 
    818  return out;
    819 }
    820 
    821 template <typename CharT>
    822 void AddStringToHash(uint32_t* hash, const CharT* chars, size_t len) {
    823  // It's tempting to use |HashString| instead of this loop, but that's
    824  // slightly different than our existing implementation for non-ropes. We
    825  // want to pretend we have a contiguous set of chars so we need to
    826  // accumulate char by char rather than generate a new hash for substring
    827  // and then accumulate that.
    828  for (size_t i = 0; i < len; i++) {
    829    *hash = mozilla::AddToHash(*hash, chars[i]);
    830  }
    831 }
    832 
    833 void AddStringToHash(uint32_t* hash, const JSString* str) {
    834  AutoCheckCannotGC nogc;
    835  const auto& s = str->asLinear();
    836  if (s.hasLatin1Chars()) {
    837    AddStringToHash(hash, s.latin1Chars(nogc), s.length());
    838  } else {
    839    AddStringToHash(hash, s.twoByteChars(nogc), s.length());
    840  }
    841 }
    842 
    843 bool JSRope::hash(uint32_t* outHash) const {
    844  Vector<const JSString*, 8, SystemAllocPolicy> nodeStack;
    845  const JSString* str = this;
    846 
    847  *outHash = 0;
    848 
    849  while (true) {
    850    if (str->isRope()) {
    851      if (!nodeStack.append(str->asRope().rightChild())) {
    852        return false;
    853      }
    854      str = str->asRope().leftChild();
    855    } else {
    856      AddStringToHash(outHash, str);
    857      if (nodeStack.empty()) {
    858        break;
    859      }
    860      str = nodeStack.popCopy();
    861    }
    862  }
    863 
    864  return true;
    865 }
    866 
    867 #if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)
    868 void JSRope::dumpOwnRepresentationFields(js::JSONPrinter& json) const {
    869  json.beginObjectProperty("leftChild");
    870  leftChild()->dumpRepresentationFields(json);
    871  json.endObject();
    872 
    873  json.beginObjectProperty("rightChild");
    874  rightChild()->dumpRepresentationFields(json);
    875  json.endObject();
    876 }
    877 #endif
    878 
    879 namespace js {
    880 
    881 template <>
    882 void CopyChars(char16_t* dest, const JSLinearString& str) {
    883  AutoCheckCannotGC nogc;
    884  if (str.hasTwoByteChars()) {
    885    PodCopy(dest, str.twoByteChars(nogc), str.length());
    886  } else {
    887    CopyAndInflateChars(dest, str.latin1Chars(nogc), str.length());
    888  }
    889 }
    890 
    891 template <>
    892 void CopyChars(Latin1Char* dest, const JSLinearString& str) {
    893  AutoCheckCannotGC nogc;
    894  if (str.hasLatin1Chars()) {
    895    PodCopy(dest, str.latin1Chars(nogc), str.length());
    896  } else {
    897    /*
    898     * When we flatten a TwoByte rope, we turn child ropes (including Latin1
    899     * ropes) into TwoByte dependent strings. If one of these strings is
    900     * also part of another Latin1 rope tree, we can have a Latin1 rope with
    901     * a TwoByte descendent and we end up here when we flatten it. Although
    902     * the chars are stored as TwoByte, we know they must be in the Latin1
    903     * range, so we can safely deflate here.
    904     */
    905    size_t len = str.length();
    906    const char16_t* chars = str.twoByteChars(nogc);
    907    auto src = Span(chars, len);
    908    MOZ_ASSERT(IsUtf16Latin1(src));
    909    LossyConvertUtf16toLatin1(src, AsWritableChars(Span(dest, len)));
    910  }
    911 }
    912 
    913 } /* namespace js */
    914 
    915 template <typename CharT>
    916 static constexpr uint32_t StringFlagsForCharType(uint32_t baseFlags) {
    917  if constexpr (std::is_same_v<CharT, char16_t>) {
    918    return baseFlags;
    919  }
    920 
    921  return baseFlags | JSString::LATIN1_CHARS_BIT;
    922 }
    923 
    924 static bool UpdateNurseryBuffersOnTransfer(js::Nursery& nursery,
    925                                           JSExtensibleString* from,
    926                                           JSString* to, void* chars,
    927                                           size_t size) {
    928  // Update the list of buffers associated with nursery cells when |buffer| is
    929  // moved from string |from| to string |to|, depending on whether those strings
    930  // are in the nursery or not.
    931 
    932  if (from->hasStringBuffer()) {
    933    // Note: addExtensibleStringBuffer is fallible so we have to call it before
    934    // removeExtensibleStringBuffer.
    935    // If both strings are in the nursery, avoid updating mallocedBufferBytes to
    936    // not trigger an unnecessary minor GC in addMallocedBufferBytes.
    937    bool updateMallocBytes = from->isTenured() || to->isTenured();
    938    if (!to->isTenured()) {
    939      auto* linear = static_cast<JSLinearString*>(to);
    940      if (!nursery.addExtensibleStringBuffer(linear, from->stringBuffer(),
    941                                             updateMallocBytes)) {
    942        return false;
    943      }
    944    }
    945    if (!from->isTenured()) {
    946      nursery.removeExtensibleStringBuffer(from, updateMallocBytes);
    947    }
    948    return true;
    949  }
    950 
    951  if (from->isTenured() && !to->isTenured()) {
    952    // Tenured leftmost child is giving its chars buffer to the
    953    // nursery-allocated root node.
    954    if (!nursery.registerMallocedBuffer(chars, size)) {
    955      return false;
    956    }
    957  } else if (!from->isTenured() && to->isTenured()) {
    958    // Leftmost child is giving its nursery-held chars buffer to a
    959    // tenured string.
    960    nursery.removeMallocedBuffer(chars, size);
    961  }
    962 
    963  return true;
    964 }
    965 
    966 static bool CanReuseLeftmostBuffer(JSString* leftmostChild, size_t wholeLength,
    967                                   bool hasTwoByteChars, bool isTenured) {
    968  if (!leftmostChild->isExtensible()) {
    969    return false;
    970  }
    971 
    972  JSExtensibleString& str = leftmostChild->asExtensible();
    973 
    974  // Don't mutate the StringBuffer if there are other references to it, possibly
    975  // on other threads.
    976  if (str.hasStringBuffer() && str.stringBuffer()->IsReadonly()) {
    977    return false;
    978  }
    979 
    980  if (str.capacity() < wholeLength ||
    981      str.hasTwoByteChars() != hasTwoByteChars) {
    982    return false;
    983  }
    984 
    985  // Don't try to reuse the buffer if the extensible string's characters are in
    986  // the nursery and the (root) rope is tenured.
    987  if (isTenured && !str.hasStringBuffer() && !str.ownsMallocedChars()) {
    988    MOZ_ASSERT(IsInsideNursery(&str));
    989    return false;
    990  }
    991 
    992  return true;
    993 }
    994 
    995 JSLinearString* JSRope::flatten(JSContext* maybecx) {
    996  mozilla::Maybe<AutoGeckoProfilerEntry> entry;
    997  if (maybecx) {
    998    entry.emplace(maybecx, "JSRope::flatten");
    999  }
   1000 
   1001  JSLinearString* str = flattenInternal();
   1002  if (!str && maybecx) {
   1003    ReportOutOfMemory(maybecx);
   1004  }
   1005 
   1006  return str;
   1007 }
   1008 
   1009 JSLinearString* JSRope::flattenInternal() {
   1010  if (zone()->needsIncrementalBarrier()) {
   1011    return flattenInternal<WithIncrementalBarrier>();
   1012  }
   1013 
   1014  return flattenInternal<NoBarrier>();
   1015 }
   1016 
   1017 template <JSRope::UsingBarrier usingBarrier>
   1018 JSLinearString* JSRope::flattenInternal() {
   1019  if (hasTwoByteChars()) {
   1020    return flattenInternal<usingBarrier, char16_t>(this);
   1021  }
   1022 
   1023  return flattenInternal<usingBarrier, Latin1Char>(this);
   1024 }
   1025 
   1026 template <JSRope::UsingBarrier usingBarrier, typename CharT>
   1027 /* static */
   1028 JSLinearString* JSRope::flattenInternal(JSRope* root) {
   1029  /*
   1030   * Consider the DAG of JSRopes rooted at |root|, with non-JSRopes as
   1031   * its leaves. Mutate the root JSRope into a JSExtensibleString containing
   1032   * the full flattened text that the root represents, and mutate all other
   1033   * JSRopes in the interior of the DAG into JSDependentStrings that refer to
   1034   * this new JSExtensibleString.
   1035   *
   1036   * If the leftmost leaf of our DAG is a JSExtensibleString, consider
   1037   * stealing its buffer for use in our new root, and transforming it into a
   1038   * JSDependentString too. Do not mutate any of the other leaves.
   1039   *
   1040   * Perform a depth-first dag traversal, splatting each node's characters
   1041   * into a contiguous buffer. Visit each rope node three times:
   1042   *   1. record position in the buffer and recurse into left child;
   1043   *   2. recurse into the right child;
   1044   *   3. transform the node into a dependent string.
   1045   * To avoid maintaining a stack, tree nodes are mutated to indicate how many
   1046   * times they have been visited. Since ropes can be dags, a node may be
   1047   * encountered multiple times during traversal. However, step 3 above leaves
   1048   * a valid dependent string, so everything works out.
   1049   *
   1050   * While ropes avoid all sorts of quadratic cases with string concatenation,
   1051   * they can't help when ropes are immediately flattened. One idiomatic case
   1052   * that we'd like to keep linear (and has traditionally been linear in SM
   1053   * and other JS engines) is:
   1054   *
   1055   *   while (...) {
   1056   *     s += ...
   1057   *     s.flatten
   1058   *   }
   1059   *
   1060   * Two behaviors accomplish this:
   1061   *
   1062   * - When the leftmost non-rope in the DAG we're flattening is a
   1063   *   JSExtensibleString with sufficient capacity to hold the entire
   1064   *   flattened string, we just flatten the DAG into its buffer. Then, when
   1065   *   we transform the root of the DAG from a JSRope into a
   1066   *   JSExtensibleString, we steal that buffer, and change the victim from a
   1067   *   JSExtensibleString to a JSDependentString. In this case, the left-hand
   1068   *   side of the string never needs to be copied.
   1069   *
   1070   * - Otherwise, we round up the total flattened size and create a fresh
   1071   *   JSExtensibleString with that much capacity. If this in turn becomes the
   1072   *   leftmost leaf of a subsequent flatten, we will hopefully be able to
   1073   *   fill it, as in the case above.
   1074   *
   1075   * Note that, even though the code for creating JSDependentStrings avoids
   1076   * creating dependents of dependents, we can create that situation here: the
   1077   * JSExtensibleStrings we transform into JSDependentStrings might have
   1078   * JSDependentStrings pointing to them already. Stealing the buffer doesn't
   1079   * change its address, only its owning JSExtensibleString, so all chars()
   1080   * pointers in the JSDependentStrings are still valid.
   1081   *
   1082   * This chain of dependent strings could be problematic if the base string
   1083   * moves, either because it was initially allocated in the nursery or it
   1084   * gets deduplicated, because you might have a dependent ->
   1085   * tenured dependent -> nursery base string, and the store buffer would
   1086   * only capture the latter edge. Prevent this case from happening by
   1087   * marking the root as nondeduplicatable if the extensible string
   1088   * optimization applied.
   1089   */
   1090  const size_t wholeLength = root->length();
   1091  size_t wholeCapacity;
   1092  CharT* wholeChars;
   1093  uint32_t newRootFlags = 0;
   1094 
   1095  AutoCheckCannotGC nogc;
   1096 
   1097  Nursery& nursery = root->runtimeFromMainThread()->gc.nursery();
   1098 
   1099  /* Find the left most string, containing the first string. */
   1100  JSRope* leftmostRope = root;
   1101  while (leftmostRope->leftChild()->isRope()) {
   1102    leftmostRope = &leftmostRope->leftChild()->asRope();
   1103  }
   1104  JSString* leftmostChild = leftmostRope->leftChild();
   1105 
   1106  bool reuseLeftmostBuffer = CanReuseLeftmostBuffer(
   1107      leftmostChild, wholeLength, std::is_same_v<CharT, char16_t>,
   1108      root->isTenured());
   1109 
   1110  bool hasStringBuffer = false;
   1111  if (reuseLeftmostBuffer) {
   1112    JSExtensibleString& left = leftmostChild->asExtensible();
   1113    wholeCapacity = left.capacity();
   1114    wholeChars = const_cast<CharT*>(left.nonInlineChars<CharT>(nogc));
   1115    hasStringBuffer = left.hasStringBuffer();
   1116 
   1117    // Nursery::registerMallocedBuffer is fallible, so attempt it first before
   1118    // doing anything irreversible.
   1119    if (!UpdateNurseryBuffersOnTransfer(nursery, &left, root, wholeChars,
   1120                                        wholeCapacity * sizeof(CharT))) {
   1121      return nullptr;
   1122    }
   1123  } else {
   1124    // If we can't reuse the leftmost child's buffer, allocate a new one.
   1125    if (!AllocCharsForFlatten(nursery, root, wholeLength, &wholeChars,
   1126                              &wholeCapacity, &hasStringBuffer)) {
   1127      return nullptr;
   1128    }
   1129  }
   1130 
   1131  JSRope* str = root;
   1132  CharT* pos = wholeChars;
   1133 
   1134  JSRope* parent = nullptr;
   1135  uint32_t parentFlag = 0;
   1136 
   1137 first_visit_node: {
   1138  MOZ_ASSERT_IF(str != root, parent && parentFlag);
   1139  MOZ_ASSERT(!str->asRope().isBeingFlattened());
   1140 
   1141  ropeBarrierDuringFlattening<usingBarrier>(str);
   1142 
   1143  JSString& left = *str->d.s.u2.left;
   1144  str->d.s.u2.parent = parent;
   1145  str->setFlagBit(parentFlag);
   1146  parent = nullptr;
   1147  parentFlag = 0;
   1148 
   1149  if (left.isRope()) {
   1150    /* Return to this node when 'left' done, then goto visit_right_child. */
   1151    parent = str;
   1152    parentFlag = FLATTEN_VISIT_RIGHT;
   1153    str = &left.asRope();
   1154    goto first_visit_node;
   1155  }
   1156  if (!(reuseLeftmostBuffer && pos == wholeChars)) {
   1157    CopyChars(pos, left.asLinear());
   1158  }
   1159  pos += left.length();
   1160 }
   1161 
   1162 visit_right_child: {
   1163  JSString& right = *str->d.s.u3.right;
   1164  if (right.isRope()) {
   1165    /* Return to this node when 'right' done, then goto finish_node. */
   1166    parent = str;
   1167    parentFlag = FLATTEN_FINISH_NODE;
   1168    str = &right.asRope();
   1169    goto first_visit_node;
   1170  }
   1171  CopyChars(pos, right.asLinear());
   1172  pos += right.length();
   1173 }
   1174 
   1175 finish_node: {
   1176  if (str == root) {
   1177    goto finish_root;
   1178  }
   1179 
   1180  MOZ_ASSERT(pos >= wholeChars);
   1181  CharT* chars = pos - str->length();
   1182  JSRope* strParent = str->d.s.u2.parent;
   1183  str->setNonInlineChars(chars, /* usesStringBuffer = */ false);
   1184 
   1185  MOZ_ASSERT(str->asRope().isBeingFlattened());
   1186  mozilla::DebugOnly<bool> visitRight = str->flags() & FLATTEN_VISIT_RIGHT;
   1187  bool finishNode = str->flags() & FLATTEN_FINISH_NODE;
   1188  MOZ_ASSERT(visitRight != finishNode);
   1189 
   1190  // This also clears the flags related to flattening.
   1191  str->setLengthAndFlags(str->length(),
   1192                         StringFlagsForCharType<CharT>(INIT_DEPENDENT_FLAGS));
   1193  str->d.s.u3.base =
   1194      reinterpret_cast<JSLinearString*>(root); /* will be true on exit */
   1195  newRootFlags |= DEPENDED_ON_BIT;
   1196 
   1197  // Every interior (rope) node in the rope's tree will be visited during
   1198  // the traversal and post-barriered here, so earlier additions of
   1199  // dependent.base -> root pointers are handled by this barrier as well.
   1200  //
   1201  // The only time post-barriers need do anything is when the root is in
   1202  // the nursery. Note that the root was a rope but will be an extensible
   1203  // string when we return, so it will not point to any strings and need
   1204  // not be barriered.
   1205  if (str->isTenured() && !root->isTenured()) {
   1206    root->storeBuffer()->putWholeCell(str);
   1207  }
   1208 
   1209  str = strParent;
   1210  if (finishNode) {
   1211    goto finish_node;
   1212  }
   1213  MOZ_ASSERT(visitRight);
   1214  goto visit_right_child;
   1215 }
   1216 
   1217 finish_root:
   1218  // We traversed all the way back up to the root so we're finished.
   1219  MOZ_ASSERT(str == root);
   1220  MOZ_ASSERT(pos == wholeChars + wholeLength);
   1221 
   1222  uint32_t flags = StringFlagsForCharType<CharT>(EXTENSIBLE_FLAGS);
   1223  if (hasStringBuffer) {
   1224    flags |= HAS_STRING_BUFFER_BIT;
   1225    wholeChars[wholeLength] = '\0';
   1226  }
   1227  root->setLengthAndFlags(wholeLength, flags);
   1228  root->setNonInlineChars(wholeChars, hasStringBuffer);
   1229  root->d.s.u3.capacity = wholeCapacity;
   1230  AddCellMemory(root, wholeCapacity * sizeof(CharT), MemoryUse::StringContents);
   1231 
   1232  if (reuseLeftmostBuffer) {
   1233    // Remove memory association for left node we're about to make into a
   1234    // dependent string.
   1235    JSString& left = *leftmostChild;
   1236    RemoveCellMemory(&left, left.allocSize(), MemoryUse::StringContents);
   1237 
   1238    // Inherit NON_DEDUP_BIT from the leftmost string.
   1239    newRootFlags |= left.flags() & NON_DEDUP_BIT;
   1240 
   1241    // Set root's DEPENDED_ON_BIT because the leftmost string is now a
   1242    // dependent.
   1243    newRootFlags |= DEPENDED_ON_BIT;
   1244 
   1245    uint32_t flags = INIT_DEPENDENT_FLAGS;
   1246    if (left.inStringToAtomCache()) {
   1247      flags |= IN_STRING_TO_ATOM_CACHE;
   1248    }
   1249    // If left was depended on, we need to make sure we preserve that. Even
   1250    // though the string that depended on left's buffer will now depend on
   1251    // root's buffer, if left is the only edge to root, replacing left with an
   1252    // atom ref would break that edge and allow root's buffer to be freed.
   1253    if (left.isDependedOn()) {
   1254      flags |= DEPENDED_ON_BIT;
   1255    }
   1256    left.setLengthAndFlags(left.length(), StringFlagsForCharType<CharT>(flags));
   1257    left.d.s.u3.base = &root->asLinear();
   1258    if (left.isTenured() && !root->isTenured()) {
   1259      // leftmost child -> root is a tenured -> nursery edge. Put the leftmost
   1260      // child in the store buffer and prevent the root's chars from moving or
   1261      // being freed (because the leftmost child may have a tenured dependent
   1262      // string that cannot be updated.)
   1263      root->storeBuffer()->putWholeCell(&left);
   1264      newRootFlags |= NON_DEDUP_BIT;
   1265    }
   1266  }
   1267 
   1268  root->setHeaderFlagBit(newRootFlags);
   1269 
   1270  return &root->asLinear();
   1271 }
   1272 
   1273 template <JSRope::UsingBarrier usingBarrier>
   1274 /* static */
   1275 inline void JSRope::ropeBarrierDuringFlattening(JSRope* rope) {
   1276  MOZ_ASSERT(!rope->isBeingFlattened());
   1277  if constexpr (usingBarrier) {
   1278    gc::PreWriteBarrierDuringFlattening(rope->leftChild());
   1279    gc::PreWriteBarrierDuringFlattening(rope->rightChild());
   1280  }
   1281 }
   1282 
   1283 template <AllowGC allowGC>
   1284 static JSLinearString* EnsureLinear(
   1285    JSContext* cx,
   1286    typename MaybeRooted<JSString*, allowGC>::HandleType string) {
   1287  JSLinearString* linear = string->ensureLinear(cx);
   1288  // Don't report an exception if GC is not allowed, just return nullptr.
   1289  if (!linear && !allowGC) {
   1290    cx->recoverFromOutOfMemory();
   1291  }
   1292  return linear;
   1293 }
   1294 
   1295 template <AllowGC allowGC>
   1296 JSString* js::ConcatStrings(
   1297    JSContext* cx, typename MaybeRooted<JSString*, allowGC>::HandleType left,
   1298    typename MaybeRooted<JSString*, allowGC>::HandleType right, gc::Heap heap) {
   1299  MOZ_ASSERT_IF(!left->isAtom(), cx->isInsideCurrentZone(left));
   1300  MOZ_ASSERT_IF(!right->isAtom(), cx->isInsideCurrentZone(right));
   1301 
   1302  size_t leftLen = left->length();
   1303  if (leftLen == 0) {
   1304    return right;
   1305  }
   1306 
   1307  size_t rightLen = right->length();
   1308  if (rightLen == 0) {
   1309    return left;
   1310  }
   1311 
   1312  size_t wholeLength = leftLen + rightLen;
   1313  if (MOZ_UNLIKELY(wholeLength > JSString::MAX_LENGTH)) {
   1314    // Don't report an exception if GC is not allowed, just return nullptr.
   1315    if (allowGC) {
   1316      js::ReportOversizedAllocation(cx, JSMSG_ALLOC_OVERFLOW);
   1317    }
   1318    return nullptr;
   1319  }
   1320 
   1321  bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars();
   1322  bool canUseInline = isLatin1
   1323                          ? JSInlineString::lengthFits<Latin1Char>(wholeLength)
   1324                          : JSInlineString::lengthFits<char16_t>(wholeLength);
   1325  if (canUseInline) {
   1326    Latin1Char* latin1Buf = nullptr;  // initialize to silence GCC warning
   1327    char16_t* twoByteBuf = nullptr;   // initialize to silence GCC warning
   1328    JSInlineString* str =
   1329        isLatin1
   1330            ? AllocateInlineString<allowGC>(cx, wholeLength, &latin1Buf, heap)
   1331            : AllocateInlineString<allowGC>(cx, wholeLength, &twoByteBuf, heap);
   1332    if (!str) {
   1333      return nullptr;
   1334    }
   1335 
   1336    AutoCheckCannotGC nogc;
   1337    JSLinearString* leftLinear = EnsureLinear<allowGC>(cx, left);
   1338    if (!leftLinear) {
   1339      return nullptr;
   1340    }
   1341    JSLinearString* rightLinear = EnsureLinear<allowGC>(cx, right);
   1342    if (!rightLinear) {
   1343      return nullptr;
   1344    }
   1345 
   1346    if (isLatin1) {
   1347      PodCopy(latin1Buf, leftLinear->latin1Chars(nogc), leftLen);
   1348      PodCopy(latin1Buf + leftLen, rightLinear->latin1Chars(nogc), rightLen);
   1349    } else {
   1350      if (leftLinear->hasTwoByteChars()) {
   1351        PodCopy(twoByteBuf, leftLinear->twoByteChars(nogc), leftLen);
   1352      } else {
   1353        CopyAndInflateChars(twoByteBuf, leftLinear->latin1Chars(nogc), leftLen);
   1354      }
   1355      if (rightLinear->hasTwoByteChars()) {
   1356        PodCopy(twoByteBuf + leftLen, rightLinear->twoByteChars(nogc),
   1357                rightLen);
   1358      } else {
   1359        CopyAndInflateChars(twoByteBuf + leftLen,
   1360                            rightLinear->latin1Chars(nogc), rightLen);
   1361      }
   1362    }
   1363 
   1364    return str;
   1365  }
   1366 
   1367  return JSRope::new_<allowGC>(cx, left, right, wholeLength, heap);
   1368 }
   1369 
   1370 template JSString* js::ConcatStrings<CanGC>(JSContext* cx, HandleString left,
   1371                                            HandleString right, gc::Heap heap);
   1372 
   1373 template JSString* js::ConcatStrings<NoGC>(JSContext* cx, JSString* const& left,
   1374                                           JSString* const& right,
   1375                                           gc::Heap heap);
   1376 
   1377 bool JSLinearString::hasCharsInCollectedNurseryRegion() const {
   1378  if (isPermanentAtom()) {
   1379    // Nursery::inCollectedRegion(void*) should only be called on the nursery's
   1380    // main thread to avoid races. Permanent atoms can be shared with worker
   1381    // threads but atoms are never allocated in the nursery.
   1382    MOZ_ASSERT(isTenured());
   1383    return false;
   1384  }
   1385  auto& nursery = runtimeFromMainThread()->gc.nursery();
   1386  if (isInline()) {
   1387    return nursery.inCollectedRegion(this);
   1388  }
   1389  return nursery.inCollectedRegion(nonInlineCharsRaw());
   1390 }
   1391 
   1392 #if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)
   1393 void JSDependentString::dumpOwnRepresentationFields(
   1394    js::JSONPrinter& json) const {
   1395  json.property("baseOffset", baseOffset());
   1396  json.beginObjectProperty("base");
   1397  base()->dumpRepresentationFields(json);
   1398  json.endObject();
   1399 }
   1400 #endif
   1401 
   1402 bool js::EqualChars(const JSLinearString* str1, const JSLinearString* str2) {
   1403  // Assert this isn't called for strings the caller should handle with a fast
   1404  // path.
   1405  MOZ_ASSERT(str1->length() == str2->length());
   1406  MOZ_ASSERT(str1 != str2);
   1407  MOZ_ASSERT(!str1->isAtom() || !str2->isAtom());
   1408 
   1409  size_t len = str1->length();
   1410 
   1411  AutoCheckCannotGC nogc;
   1412  if (str1->hasTwoByteChars()) {
   1413    if (str2->hasTwoByteChars()) {
   1414      return EqualChars(str1->twoByteChars(nogc), str2->twoByteChars(nogc),
   1415                        len);
   1416    }
   1417 
   1418    return EqualChars(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
   1419  }
   1420 
   1421  if (str2->hasLatin1Chars()) {
   1422    return EqualChars(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len);
   1423  }
   1424 
   1425  return EqualChars(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
   1426 }
   1427 
   1428 bool js::HasSubstringAt(const JSLinearString* text, const JSLinearString* pat,
   1429                        size_t start) {
   1430  MOZ_ASSERT(start + pat->length() <= text->length());
   1431 
   1432  size_t patLen = pat->length();
   1433 
   1434  AutoCheckCannotGC nogc;
   1435  if (text->hasLatin1Chars()) {
   1436    const Latin1Char* textChars = text->latin1Chars(nogc) + start;
   1437    if (pat->hasLatin1Chars()) {
   1438      return EqualChars(textChars, pat->latin1Chars(nogc), patLen);
   1439    }
   1440 
   1441    return EqualChars(textChars, pat->twoByteChars(nogc), patLen);
   1442  }
   1443 
   1444  const char16_t* textChars = text->twoByteChars(nogc) + start;
   1445  if (pat->hasTwoByteChars()) {
   1446    return EqualChars(textChars, pat->twoByteChars(nogc), patLen);
   1447  }
   1448 
   1449  return EqualChars(pat->latin1Chars(nogc), textChars, patLen);
   1450 }
   1451 
   1452 bool js::EqualStrings(JSContext* cx, JSString* str1, JSString* str2,
   1453                      bool* result) {
   1454  if (str1 == str2) {
   1455    *result = true;
   1456    return true;
   1457  }
   1458  if (str1->length() != str2->length()) {
   1459    *result = false;
   1460    return true;
   1461  }
   1462  if (str1->isAtom() && str2->isAtom()) {
   1463    *result = false;
   1464    return true;
   1465  }
   1466 
   1467  JSLinearString* linear1 = str1->ensureLinear(cx);
   1468  if (!linear1) {
   1469    return false;
   1470  }
   1471  JSLinearString* linear2 = str2->ensureLinear(cx);
   1472  if (!linear2) {
   1473    return false;
   1474  }
   1475 
   1476  *result = EqualChars(linear1, linear2);
   1477  return true;
   1478 }
   1479 
   1480 bool js::EqualStrings(const JSLinearString* str1, const JSLinearString* str2) {
   1481  if (str1 == str2) {
   1482    return true;
   1483  }
   1484  if (str1->length() != str2->length()) {
   1485    return false;
   1486  }
   1487  if (str1->isAtom() && str2->isAtom()) {
   1488    return false;
   1489  }
   1490  return EqualChars(str1, str2);
   1491 }
   1492 
   1493 int32_t js::CompareChars(const char16_t* s1, size_t len1,
   1494                         const JSLinearString* s2) {
   1495  AutoCheckCannotGC nogc;
   1496  return s2->hasLatin1Chars()
   1497             ? CompareChars(s1, len1, s2->latin1Chars(nogc), s2->length())
   1498             : CompareChars(s1, len1, s2->twoByteChars(nogc), s2->length());
   1499 }
   1500 
   1501 static int32_t CompareStringsImpl(const JSLinearString* str1,
   1502                                  const JSLinearString* str2) {
   1503  size_t len1 = str1->length();
   1504  size_t len2 = str2->length();
   1505 
   1506  AutoCheckCannotGC nogc;
   1507  if (str1->hasLatin1Chars()) {
   1508    const Latin1Char* chars1 = str1->latin1Chars(nogc);
   1509    return str2->hasLatin1Chars()
   1510               ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
   1511               : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
   1512  }
   1513 
   1514  const char16_t* chars1 = str1->twoByteChars(nogc);
   1515  return str2->hasLatin1Chars()
   1516             ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
   1517             : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
   1518 }
   1519 
   1520 bool js::CompareStrings(JSContext* cx, JSString* str1, JSString* str2,
   1521                        int32_t* result) {
   1522  MOZ_ASSERT(str1);
   1523  MOZ_ASSERT(str2);
   1524 
   1525  if (str1 == str2) {
   1526    *result = 0;
   1527    return true;
   1528  }
   1529 
   1530  JSLinearString* linear1 = str1->ensureLinear(cx);
   1531  if (!linear1) {
   1532    return false;
   1533  }
   1534 
   1535  JSLinearString* linear2 = str2->ensureLinear(cx);
   1536  if (!linear2) {
   1537    return false;
   1538  }
   1539 
   1540  *result = CompareStringsImpl(linear1, linear2);
   1541  return true;
   1542 }
   1543 
   1544 int32_t js::CompareStrings(const JSLinearString* str1,
   1545                           const JSLinearString* str2) {
   1546  MOZ_ASSERT(str1);
   1547  MOZ_ASSERT(str2);
   1548 
   1549  if (str1 == str2) {
   1550    return 0;
   1551  }
   1552  return CompareStringsImpl(str1, str2);
   1553 }
   1554 
   1555 int32_t js::CompareStrings(const JSOffThreadAtom* str1,
   1556                           const JSOffThreadAtom* str2) {
   1557  MOZ_ASSERT(str1);
   1558  MOZ_ASSERT(str2);
   1559 
   1560  if (str1 == str2) {
   1561    return 0;
   1562  }
   1563 
   1564  size_t len1 = str1->length();
   1565  size_t len2 = str2->length();
   1566 
   1567  AutoCheckCannotGC nogc;
   1568  if (str1->hasLatin1Chars()) {
   1569    const Latin1Char* chars1 = str1->latin1Chars(nogc);
   1570    return str2->hasLatin1Chars()
   1571               ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
   1572               : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
   1573  }
   1574 
   1575  const char16_t* chars1 = str1->twoByteChars(nogc);
   1576  return str2->hasLatin1Chars()
   1577             ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
   1578             : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
   1579 }
   1580 
   1581 bool js::StringIsAscii(const JSLinearString* str) {
   1582  JS::AutoCheckCannotGC nogc;
   1583  if (str->hasLatin1Chars()) {
   1584    return mozilla::IsAscii(
   1585        AsChars(Span(str->latin1Chars(nogc), str->length())));
   1586  }
   1587  return mozilla::IsAscii(Span(str->twoByteChars(nogc), str->length()));
   1588 }
   1589 
   1590 bool js::StringEqualsAscii(const JSLinearString* str, const char* asciiBytes) {
   1591  return StringEqualsAscii(str, asciiBytes, strlen(asciiBytes));
   1592 }
   1593 
   1594 bool js::StringEqualsAscii(const JSLinearString* str, const char* asciiBytes,
   1595                           size_t length) {
   1596  MOZ_ASSERT(JS::StringIsASCII(Span(asciiBytes, length)));
   1597 
   1598  if (length != str->length()) {
   1599    return false;
   1600  }
   1601 
   1602  const Latin1Char* latin1 = reinterpret_cast<const Latin1Char*>(asciiBytes);
   1603 
   1604  AutoCheckCannotGC nogc;
   1605  return str->hasLatin1Chars()
   1606             ? EqualChars(latin1, str->latin1Chars(nogc), length)
   1607             : EqualChars(latin1, str->twoByteChars(nogc), length);
   1608 }
   1609 
   1610 template <typename CharT>
   1611 bool js::CheckStringIsIndex(const CharT* s, size_t length, uint32_t* indexp) {
   1612  MOZ_ASSERT(length > 0);
   1613  MOZ_ASSERT(length <= UINT32_CHAR_BUFFER_LENGTH);
   1614  MOZ_ASSERT(IsAsciiDigit(*s),
   1615             "caller's fast path must have checked first char");
   1616 
   1617  RangedPtr<const CharT> cp(s, length);
   1618  const RangedPtr<const CharT> end(s + length, s, length);
   1619 
   1620  uint32_t index = AsciiDigitToNumber(*cp++);
   1621  uint32_t oldIndex = 0;
   1622  uint32_t c = 0;
   1623 
   1624  if (index != 0) {
   1625    // Consume remaining characters only if the first character isn't '0'.
   1626    while (cp < end && IsAsciiDigit(*cp)) {
   1627      oldIndex = index;
   1628      c = AsciiDigitToNumber(*cp);
   1629      index = 10 * index + c;
   1630      cp++;
   1631    }
   1632  }
   1633 
   1634  // It's not an integer index if there are characters after the number.
   1635  if (cp != end) {
   1636    return false;
   1637  }
   1638 
   1639  // Look out for "4294967295" and larger-number strings that fit in
   1640  // UINT32_CHAR_BUFFER_LENGTH: only unsigned 32-bit integers less than or equal
   1641  // to MAX_ARRAY_INDEX shall pass.
   1642  if (oldIndex < MAX_ARRAY_INDEX / 10 ||
   1643      (oldIndex == MAX_ARRAY_INDEX / 10 && c <= (MAX_ARRAY_INDEX % 10))) {
   1644    MOZ_ASSERT(index <= MAX_ARRAY_INDEX);
   1645    *indexp = index;
   1646    return true;
   1647  }
   1648 
   1649  return false;
   1650 }
   1651 
   1652 template bool js::CheckStringIsIndex(const Latin1Char* s, size_t length,
   1653                                     uint32_t* indexp);
   1654 template bool js::CheckStringIsIndex(const char16_t* s, size_t length,
   1655                                     uint32_t* indexp);
   1656 
   1657 template <typename CharT>
   1658 static uint32_t AtomCharsToIndex(const CharT* s, size_t length) {
   1659  // Chars are known to be a valid index value (as determined by
   1660  // CheckStringIsIndex) that didn't fit in the "index value" bits in the
   1661  // header.
   1662 
   1663  MOZ_ASSERT(length > 0);
   1664  MOZ_ASSERT(length <= UINT32_CHAR_BUFFER_LENGTH);
   1665 
   1666  RangedPtr<const CharT> cp(s, length);
   1667  const RangedPtr<const CharT> end(s + length, s, length);
   1668 
   1669  MOZ_ASSERT(IsAsciiDigit(*cp));
   1670  uint32_t index = AsciiDigitToNumber(*cp++);
   1671  MOZ_ASSERT(index != 0);
   1672 
   1673  while (cp < end) {
   1674    MOZ_ASSERT(IsAsciiDigit(*cp));
   1675    index = 10 * index + AsciiDigitToNumber(*cp);
   1676    cp++;
   1677  }
   1678 
   1679  MOZ_ASSERT(index <= MAX_ARRAY_INDEX);
   1680  return index;
   1681 }
   1682 
   1683 uint32_t JSAtom::getIndexSlow() const {
   1684  MOZ_ASSERT(isIndex());
   1685  MOZ_ASSERT(!hasIndexValue());
   1686 
   1687  size_t len = length();
   1688 
   1689  AutoCheckCannotGC nogc;
   1690  return hasLatin1Chars() ? AtomCharsToIndex(latin1Chars(nogc), len)
   1691                          : AtomCharsToIndex(twoByteChars(nogc), len);
   1692 }
   1693 
   1694 uint32_t JSOffThreadAtom::getIndexSlow() const {
   1695  MOZ_ASSERT(isIndex());
   1696  MOZ_ASSERT(!hasIndexValue());
   1697 
   1698  size_t len = length();
   1699 
   1700  AutoCheckCannotGC nogc;
   1701  return hasLatin1Chars() ? AtomCharsToIndex(latin1Chars(nogc), len)
   1702                          : AtomCharsToIndex(twoByteChars(nogc), len);
   1703 }
   1704 
   1705 // Ensure that the incoming s.chars pointer is stable, as in, it cannot be
   1706 // changed even across a GC. That requires that the string that owns the chars
   1707 // not be collected or deduplicated.
   1708 void AutoStableStringChars::holdStableChars(JSLinearString* s) {
   1709  while (s->hasBase()) {
   1710    s = s->base();
   1711  }
   1712  if (!s->isTenured()) {
   1713    s->setNonDeduplicatable();
   1714  }
   1715  s_ = s;
   1716 }
   1717 
   1718 bool AutoStableStringChars::init(JSContext* cx, JSString* s) {
   1719  JSLinearString* linearString = s->ensureLinear(cx);
   1720  if (!linearString) {
   1721    return false;
   1722  }
   1723 
   1724  linearString->setDependedOn();
   1725 
   1726  MOZ_ASSERT(state_ == Uninitialized);
   1727  length_ = linearString->length();
   1728 
   1729  // Inline and nursery-allocated chars may move during a GC, so copy them
   1730  // out into a temporary malloced buffer. Note that we cannot update the
   1731  // string itself with a malloced buffer, because there may be dependent
   1732  // strings that are using the original chars.
   1733  if (linearString->hasMovableChars()) {
   1734    return linearString->hasTwoByteChars() ? copyTwoByteChars(cx, linearString)
   1735                                           : copyLatin1Chars(cx, linearString);
   1736  }
   1737 
   1738  if (linearString->hasLatin1Chars()) {
   1739    state_ = Latin1;
   1740    latin1Chars_ = linearString->rawLatin1Chars();
   1741  } else {
   1742    state_ = TwoByte;
   1743    twoByteChars_ = linearString->rawTwoByteChars();
   1744  }
   1745 
   1746  holdStableChars(linearString);
   1747  return true;
   1748 }
   1749 
   1750 bool AutoStableStringChars::initTwoByte(JSContext* cx, JSString* s) {
   1751  JSLinearString* linearString = s->ensureLinear(cx);
   1752  if (!linearString) {
   1753    return false;
   1754  }
   1755 
   1756  linearString->setDependedOn();
   1757 
   1758  MOZ_ASSERT(state_ == Uninitialized);
   1759  length_ = linearString->length();
   1760 
   1761  if (linearString->hasLatin1Chars()) {
   1762    return copyAndInflateLatin1Chars(cx, linearString);
   1763  }
   1764 
   1765  // Copy movable chars since they may be moved by GC (see above).
   1766  if (linearString->hasMovableChars()) {
   1767    return copyTwoByteChars(cx, linearString);
   1768  }
   1769 
   1770  state_ = TwoByte;
   1771  twoByteChars_ = linearString->rawTwoByteChars();
   1772 
   1773  holdStableChars(linearString);
   1774  return true;
   1775 }
   1776 
   1777 template <typename T>
   1778 T* AutoStableStringChars::allocOwnChars(JSContext* cx, size_t count) {
   1779  static_assert(
   1780      InlineCapacity >=
   1781              sizeof(JS::Latin1Char) * JSFatInlineString::MAX_LENGTH_LATIN1 &&
   1782          InlineCapacity >=
   1783              sizeof(char16_t) * JSFatInlineString::MAX_LENGTH_TWO_BYTE,
   1784      "InlineCapacity too small to hold fat inline strings");
   1785 
   1786  static_assert(JSString::MAX_LENGTH * sizeof(T) >= JSString::MAX_LENGTH,
   1787                "Size calculation can overflow");
   1788  MOZ_ASSERT(count <= JSString::MAX_LENGTH);
   1789  size_t size = sizeof(T) * count;
   1790 
   1791  ownChars_.emplace(cx);
   1792  if (!ownChars_->resize(size)) {
   1793    ownChars_.reset();
   1794    return nullptr;
   1795  }
   1796 
   1797  return reinterpret_cast<T*>(ownChars_->begin());
   1798 }
   1799 
   1800 bool AutoStableStringChars::copyAndInflateLatin1Chars(
   1801    JSContext* cx, JSLinearString* linearString) {
   1802  MOZ_ASSERT(state_ == Uninitialized);
   1803  MOZ_ASSERT(s_ == nullptr);
   1804 
   1805  char16_t* chars = allocOwnChars<char16_t>(cx, length_);
   1806  if (!chars) {
   1807    return false;
   1808  }
   1809 
   1810  // Copy |src[0..length]| to |dest[0..length]| when copying doesn't narrow and
   1811  // therefore can't lose information.
   1812  auto src = AsChars(Span(linearString->rawLatin1Chars(), length_));
   1813  auto dest = Span(chars, length_);
   1814  ConvertLatin1toUtf16(src, dest);
   1815 
   1816  state_ = TwoByte;
   1817  twoByteChars_ = chars;
   1818  s_ = linearString;
   1819  return true;
   1820 }
   1821 
   1822 bool AutoStableStringChars::copyLatin1Chars(JSContext* cx,
   1823                                            JSLinearString* linearString) {
   1824  MOZ_ASSERT(state_ == Uninitialized);
   1825  MOZ_ASSERT(s_ == nullptr);
   1826 
   1827  JS::Latin1Char* chars = allocOwnChars<JS::Latin1Char>(cx, length_);
   1828  if (!chars) {
   1829    return false;
   1830  }
   1831 
   1832  PodCopy(chars, linearString->rawLatin1Chars(), length_);
   1833 
   1834  state_ = Latin1;
   1835  latin1Chars_ = chars;
   1836  s_ = linearString;
   1837  return true;
   1838 }
   1839 
   1840 bool AutoStableStringChars::copyTwoByteChars(JSContext* cx,
   1841                                             JSLinearString* linearString) {
   1842  MOZ_ASSERT(state_ == Uninitialized);
   1843  MOZ_ASSERT(s_ == nullptr);
   1844 
   1845  char16_t* chars = allocOwnChars<char16_t>(cx, length_);
   1846  if (!chars) {
   1847    return false;
   1848  }
   1849 
   1850  PodCopy(chars, linearString->rawTwoByteChars(), length_);
   1851 
   1852  state_ = TwoByte;
   1853  twoByteChars_ = chars;
   1854  s_ = linearString;
   1855  return true;
   1856 }
   1857 
   1858 template <>
   1859 bool JS::SourceText<char16_t>::initMaybeBorrowed(
   1860    JSContext* cx, JS::AutoStableStringChars& linearChars) {
   1861  MOZ_ASSERT(linearChars.isTwoByte(),
   1862             "AutoStableStringChars must be initialized with char16_t");
   1863 
   1864  const char16_t* chars = linearChars.twoByteChars();
   1865  size_t length = linearChars.length();
   1866  JS::SourceOwnership ownership = linearChars.maybeGiveOwnershipToCaller()
   1867                                      ? JS::SourceOwnership::TakeOwnership
   1868                                      : JS::SourceOwnership::Borrowed;
   1869  return initImpl(cx, chars, length, ownership);
   1870 }
   1871 
   1872 template <>
   1873 bool JS::SourceText<char16_t>::initMaybeBorrowed(
   1874    JS::FrontendContext* fc, JS::AutoStableStringChars& linearChars) {
   1875  MOZ_ASSERT(linearChars.isTwoByte(),
   1876             "AutoStableStringChars must be initialized with char16_t");
   1877 
   1878  const char16_t* chars = linearChars.twoByteChars();
   1879  size_t length = linearChars.length();
   1880  JS::SourceOwnership ownership = linearChars.maybeGiveOwnershipToCaller()
   1881                                      ? JS::SourceOwnership::TakeOwnership
   1882                                      : JS::SourceOwnership::Borrowed;
   1883  return initImpl(fc, chars, length, ownership);
   1884 }
   1885 
   1886 #if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)
   1887 void JSAtom::dump(js::GenericPrinter& out) {
   1888  out.printf("JSAtom* (%p) = ", (void*)this);
   1889  this->JSString::dump(out);
   1890 }
   1891 
   1892 void JSAtom::dump() {
   1893  Fprinter out(stderr);
   1894  dump(out);
   1895 }
   1896 
   1897 void JSExternalString::dumpOwnRepresentationFields(
   1898    js::JSONPrinter& json) const {
   1899  json.formatProperty("callbacks", "(JSExternalStringCallbacks*)0x%p",
   1900                      callbacks());
   1901 }
   1902 #endif /* defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW) */
   1903 
   1904 template <JS::ContractBaseChain contract>
   1905 static JSLinearString* NewDependentStringHelper(JSContext* cx,
   1906                                                JSString* baseArg, size_t start,
   1907                                                size_t length, gc::Heap heap) {
   1908  if (length == 0) {
   1909    return cx->emptyString();
   1910  }
   1911 
   1912  JSLinearString* base = baseArg->ensureLinear(cx);
   1913  if (!base) {
   1914    return nullptr;
   1915  }
   1916 
   1917  if (start == 0 && length == base->length()) {
   1918    return base;
   1919  }
   1920 
   1921  bool useInline;
   1922  if (base->hasTwoByteChars()) {
   1923    AutoCheckCannotGC nogc;
   1924    const char16_t* chars = base->twoByteChars(nogc) + start;
   1925    if (JSLinearString* staticStr = cx->staticStrings().lookup(chars, length)) {
   1926      return staticStr;
   1927    }
   1928    useInline = JSInlineString::lengthFits<char16_t>(length);
   1929  } else {
   1930    AutoCheckCannotGC nogc;
   1931    const Latin1Char* chars = base->latin1Chars(nogc) + start;
   1932    if (JSLinearString* staticStr = cx->staticStrings().lookup(chars, length)) {
   1933      return staticStr;
   1934    }
   1935    useInline = JSInlineString::lengthFits<Latin1Char>(length);
   1936  }
   1937 
   1938  if (useInline) {
   1939    Rooted<JSLinearString*> rootedBase(cx, base);
   1940 
   1941    // Do not create a dependent string that would fit into an inline string.
   1942    // First, that could create a string dependent on an inline base string's
   1943    // chars, which would be an awkward moving-GC hazard. Second, this makes
   1944    // it more likely to have a very short string keep a very long string alive.
   1945    if (base->hasTwoByteChars()) {
   1946      return NewInlineString<char16_t>(cx, rootedBase, start, length, heap);
   1947    }
   1948    return NewInlineString<Latin1Char>(cx, rootedBase, start, length, heap);
   1949  }
   1950 
   1951  return JSDependentString::newImpl_<contract>(cx, base, start, length, heap);
   1952 }
   1953 
   1954 JSLinearString* js::NewDependentString(JSContext* cx, JSString* baseArg,
   1955                                       size_t start, size_t length,
   1956                                       gc::Heap heap) {
   1957  return NewDependentStringHelper<JS::ContractBaseChain::Contract>(
   1958      cx, baseArg, start, length, heap);
   1959 }
   1960 
   1961 JSLinearString* js::NewDependentStringForTesting(JSContext* cx,
   1962                                                 JSString* baseArg,
   1963                                                 size_t start, size_t length,
   1964                                                 JS::ContractBaseChain contract,
   1965                                                 gc::Heap heap) {
   1966  if (contract == JS::ContractBaseChain::Contract) {
   1967    return NewDependentStringHelper<JS::ContractBaseChain::Contract>(
   1968        cx, baseArg, start, length, heap);
   1969  }
   1970  return NewDependentStringHelper<JS::ContractBaseChain::AllowLong>(
   1971      cx, baseArg, start, length, heap);
   1972 }
   1973 
   1974 static constexpr bool CanStoreCharsAsLatin1(const JS::Latin1Char* s,
   1975                                            size_t length) {
   1976  return true;
   1977 }
   1978 
   1979 static inline bool CanStoreCharsAsLatin1(const char16_t* s, size_t length) {
   1980  return IsUtf16Latin1(Span(s, length));
   1981 }
   1982 
   1983 /**
   1984 * Copy |src[0..length]| to |dest[0..length]| when copying *does* narrow, but
   1985 * the user guarantees every runtime |src[i]| value can be stored without change
   1986 * of value in |dest[i]|.
   1987 */
   1988 static inline void FillFromCompatible(unsigned char* dest, const char16_t* src,
   1989                                      size_t length) {
   1990  LossyConvertUtf16toLatin1(Span(src, length),
   1991                            AsWritableChars(Span(dest, length)));
   1992 }
   1993 
   1994 template <AllowGC allowGC>
   1995 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineStringDeflated(
   1996    JSContext* cx, const mozilla::Range<const char16_t>& chars,
   1997    gc::Heap heap = gc::Heap::Default) {
   1998  size_t len = chars.length();
   1999  Latin1Char* storage;
   2000  JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage, heap);
   2001  if (!str) {
   2002    return nullptr;
   2003  }
   2004 
   2005  MOZ_ASSERT(CanStoreCharsAsLatin1(chars.begin().get(), len));
   2006  FillFromCompatible(storage, chars.begin().get(), len);
   2007  return str;
   2008 }
   2009 
   2010 template <AllowGC allowGC>
   2011 static JSLinearString* NewStringDeflated(JSContext* cx, const char16_t* s,
   2012                                         size_t n, gc::Heap heap) {
   2013  if (JSLinearString* str = TryEmptyOrStaticString(cx, s, n)) {
   2014    return str;
   2015  }
   2016 
   2017  if (JSInlineString::lengthFits<Latin1Char>(n)) {
   2018    return NewInlineStringDeflated<allowGC>(
   2019        cx, mozilla::Range<const char16_t>(s, n), heap);
   2020  }
   2021 
   2022  JS::Rooted<JSString::OwnedChars<Latin1Char>> news(
   2023      cx, AllocChars<Latin1Char>(cx, n, heap));
   2024  if (!news) {
   2025    if (!allowGC) {
   2026      cx->recoverFromOutOfMemory();
   2027    }
   2028    return nullptr;
   2029  }
   2030 
   2031  MOZ_ASSERT(CanStoreCharsAsLatin1(s, n));
   2032  FillFromCompatible(news.data(), s, n);
   2033 
   2034  return JSLinearString::new_<allowGC, Latin1Char>(cx, &news, heap);
   2035 }
   2036 
   2037 static MOZ_ALWAYS_INLINE JSAtom* NewInlineAtomDeflated(JSContext* cx,
   2038                                                       const char16_t* chars,
   2039                                                       size_t length,
   2040                                                       js::HashNumber hash) {
   2041  Latin1Char* storage;
   2042  JSAtom* str = AllocateInlineAtom(cx, length, &storage, hash);
   2043  if (!str) {
   2044    return nullptr;
   2045  }
   2046 
   2047  MOZ_ASSERT(CanStoreCharsAsLatin1(chars, length));
   2048  FillFromCompatible(storage, chars, length);
   2049  return str;
   2050 }
   2051 
   2052 static JSAtom* NewAtomDeflatedValidLength(JSContext* cx, const char16_t* s,
   2053                                          size_t n, js::HashNumber hash) {
   2054  if (JSAtom::lengthFitsInline<Latin1Char>(n)) {
   2055    return NewInlineAtomDeflated(cx, s, n, hash);
   2056  }
   2057 
   2058  JSString::OwnedChars<Latin1Char> newChars(
   2059      AllocAtomCharsValidLength<Latin1Char>(cx, n));
   2060  if (!newChars) {
   2061    return nullptr;
   2062  }
   2063 
   2064  MOZ_ASSERT(CanStoreCharsAsLatin1(s, n));
   2065  FillFromCompatible(newChars.data(), s, n);
   2066 
   2067  return JSAtom::newValidLength<Latin1Char>(cx, newChars, hash);
   2068 }
   2069 
   2070 template <AllowGC allowGC, typename CharT>
   2071 JSLinearString* js::NewStringDontDeflate(
   2072    JSContext* cx, UniquePtr<CharT[], JS::FreePolicy> chars, size_t length,
   2073    gc::Heap heap) {
   2074  if (JSLinearString* str = TryEmptyOrStaticString(cx, chars.get(), length)) {
   2075    return str;
   2076  }
   2077 
   2078  if (JSInlineString::lengthFits<CharT>(length)) {
   2079    // |chars.get()| is safe because 1) |NewInlineString| necessarily *copies*,
   2080    // and 2) |chars| frees its contents only when this function returns.
   2081    return NewInlineString<allowGC>(
   2082        cx, mozilla::Range<const CharT>(chars.get(), length), heap);
   2083  }
   2084 
   2085  JS::Rooted<JSString::OwnedChars<CharT>> ownedChars(cx, std::move(chars),
   2086                                                     length);
   2087  return JSLinearString::new_<allowGC, CharT>(cx, &ownedChars, heap);
   2088 }
   2089 
   2090 template JSLinearString* js::NewStringDontDeflate<CanGC>(
   2091    JSContext* cx, UniqueTwoByteChars chars, size_t length, gc::Heap heap);
   2092 
   2093 template JSLinearString* js::NewStringDontDeflate<NoGC>(
   2094    JSContext* cx, UniqueTwoByteChars chars, size_t length, gc::Heap heap);
   2095 
   2096 template JSLinearString* js::NewStringDontDeflate<CanGC>(
   2097    JSContext* cx, UniqueLatin1Chars chars, size_t length, gc::Heap heap);
   2098 
   2099 template JSLinearString* js::NewStringDontDeflate<NoGC>(JSContext* cx,
   2100                                                        UniqueLatin1Chars chars,
   2101                                                        size_t length,
   2102                                                        gc::Heap heap);
   2103 
   2104 template <AllowGC allowGC, typename CharT>
   2105 JSLinearString* js::NewString(JSContext* cx,
   2106                              UniquePtr<CharT[], JS::FreePolicy> chars,
   2107                              size_t length, gc::Heap heap) {
   2108  if constexpr (std::is_same_v<CharT, char16_t>) {
   2109    if (CanStoreCharsAsLatin1(chars.get(), length)) {
   2110      // Deflating copies from |chars.get()| and lets |chars| be freed on
   2111      // return.
   2112      return NewStringDeflated<allowGC>(cx, chars.get(), length, heap);
   2113    }
   2114  }
   2115 
   2116  return NewStringDontDeflate<allowGC>(cx, std::move(chars), length, heap);
   2117 }
   2118 
   2119 template JSLinearString* js::NewString<CanGC>(JSContext* cx,
   2120                                              UniqueTwoByteChars chars,
   2121                                              size_t length, gc::Heap heap);
   2122 
   2123 template JSLinearString* js::NewString<NoGC>(JSContext* cx,
   2124                                             UniqueTwoByteChars chars,
   2125                                             size_t length, gc::Heap heap);
   2126 
   2127 template JSLinearString* js::NewString<CanGC>(JSContext* cx,
   2128                                              UniqueLatin1Chars chars,
   2129                                              size_t length, gc::Heap heap);
   2130 
   2131 template JSLinearString* js::NewString<NoGC>(JSContext* cx,
   2132                                             UniqueLatin1Chars chars,
   2133                                             size_t length, gc::Heap heap);
   2134 
   2135 namespace js {
   2136 
   2137 template <AllowGC allowGC, typename CharT>
   2138 JSLinearString* NewStringCopyNDontDeflateNonStaticValidLength(JSContext* cx,
   2139                                                              const CharT* s,
   2140                                                              size_t n,
   2141                                                              gc::Heap heap) {
   2142  if (JSInlineString::lengthFits<CharT>(n)) {
   2143    return NewInlineString<allowGC>(cx, mozilla::Range<const CharT>(s, n),
   2144                                    heap);
   2145  }
   2146 
   2147  Rooted<JSString::OwnedChars<CharT>> news(cx,
   2148                                           ::AllocChars<CharT>(cx, n, heap));
   2149  if (!news) {
   2150    if (!allowGC) {
   2151      cx->recoverFromOutOfMemory();
   2152    }
   2153    return nullptr;
   2154  }
   2155 
   2156  PodCopy(news.data(), s, n);
   2157 
   2158  return JSLinearString::newValidLength<allowGC, CharT>(cx, &news, heap);
   2159 }
   2160 
   2161 template JSLinearString* NewStringCopyNDontDeflateNonStaticValidLength<CanGC>(
   2162    JSContext* cx, const char16_t* s, size_t n, gc::Heap heap);
   2163 
   2164 template JSLinearString* NewStringCopyNDontDeflateNonStaticValidLength<NoGC>(
   2165    JSContext* cx, const char16_t* s, size_t n, gc::Heap heap);
   2166 
   2167 template JSLinearString* NewStringCopyNDontDeflateNonStaticValidLength<CanGC>(
   2168    JSContext* cx, const Latin1Char* s, size_t n, gc::Heap heap);
   2169 
   2170 template JSLinearString* NewStringCopyNDontDeflateNonStaticValidLength<NoGC>(
   2171    JSContext* cx, const Latin1Char* s, size_t n, gc::Heap heap);
   2172 
   2173 template <AllowGC allowGC, typename CharT>
   2174 JSLinearString* NewStringCopyNDontDeflate(JSContext* cx, const CharT* s,
   2175                                          size_t n, gc::Heap heap) {
   2176  if (JSLinearString* str = TryEmptyOrStaticString(cx, s, n)) {
   2177    return str;
   2178  }
   2179 
   2180  if (MOZ_UNLIKELY(!JSLinearString::validateLength(cx, n))) {
   2181    return nullptr;
   2182  }
   2183 
   2184  return NewStringCopyNDontDeflateNonStaticValidLength<allowGC>(cx, s, n, heap);
   2185 }
   2186 
   2187 template JSLinearString* NewStringCopyNDontDeflate<CanGC>(JSContext* cx,
   2188                                                          const char16_t* s,
   2189                                                          size_t n,
   2190                                                          gc::Heap heap);
   2191 
   2192 template JSLinearString* NewStringCopyNDontDeflate<NoGC>(JSContext* cx,
   2193                                                         const char16_t* s,
   2194                                                         size_t n,
   2195                                                         gc::Heap heap);
   2196 
   2197 template JSLinearString* NewStringCopyNDontDeflate<CanGC>(JSContext* cx,
   2198                                                          const Latin1Char* s,
   2199                                                          size_t n,
   2200                                                          gc::Heap heap);
   2201 
   2202 template JSLinearString* NewStringCopyNDontDeflate<NoGC>(JSContext* cx,
   2203                                                         const Latin1Char* s,
   2204                                                         size_t n,
   2205                                                         gc::Heap heap);
   2206 
   2207 JSLinearString* NewLatin1StringZ(JSContext* cx, UniqueChars chars,
   2208                                 gc::Heap heap) {
   2209  size_t length = strlen(chars.get());
   2210  UniqueLatin1Chars latin1(reinterpret_cast<Latin1Char*>(chars.release()));
   2211  return NewString<CanGC>(cx, std::move(latin1), length, heap);
   2212 }
   2213 
   2214 template <AllowGC allowGC, typename CharT>
   2215 JSLinearString* NewStringCopyN(JSContext* cx, const CharT* s, size_t n,
   2216                               gc::Heap heap) {
   2217  if constexpr (std::is_same_v<CharT, char16_t>) {
   2218    if (CanStoreCharsAsLatin1(s, n)) {
   2219      return NewStringDeflated<allowGC>(cx, s, n, heap);
   2220    }
   2221  }
   2222 
   2223  return NewStringCopyNDontDeflate<allowGC>(cx, s, n, heap);
   2224 }
   2225 
   2226 template JSLinearString* NewStringCopyN<CanGC>(JSContext* cx, const char16_t* s,
   2227                                               size_t n, gc::Heap heap);
   2228 
   2229 template JSLinearString* NewStringCopyN<NoGC>(JSContext* cx, const char16_t* s,
   2230                                              size_t n, gc::Heap heap);
   2231 
   2232 template JSLinearString* NewStringCopyN<CanGC>(JSContext* cx,
   2233                                               const Latin1Char* s, size_t n,
   2234                                               gc::Heap heap);
   2235 
   2236 template JSLinearString* NewStringCopyN<NoGC>(JSContext* cx,
   2237                                              const Latin1Char* s, size_t n,
   2238                                              gc::Heap heap);
   2239 
   2240 template <typename CharT>
   2241 JSAtom* NewAtomCopyNDontDeflateValidLength(JSContext* cx, const CharT* s,
   2242                                           size_t n, js::HashNumber hash) {
   2243  if constexpr (std::is_same_v<CharT, char16_t>) {
   2244    MOZ_ASSERT(!CanStoreCharsAsLatin1(s, n));
   2245  }
   2246 
   2247  if (JSAtom::lengthFitsInline<CharT>(n)) {
   2248    return NewInlineAtom(cx, s, n, hash);
   2249  }
   2250 
   2251  JSString::OwnedChars<CharT> newChars(AllocAtomCharsValidLength<CharT>(cx, n));
   2252  if (!newChars) {
   2253    return nullptr;
   2254  }
   2255 
   2256  PodCopy(newChars.data(), s, n);
   2257 
   2258  return JSAtom::newValidLength<CharT>(cx, newChars, hash);
   2259 }
   2260 
   2261 template JSAtom* NewAtomCopyNDontDeflateValidLength(JSContext* cx,
   2262                                                    const char16_t* s, size_t n,
   2263                                                    js::HashNumber hash);
   2264 
   2265 template JSAtom* NewAtomCopyNDontDeflateValidLength(JSContext* cx,
   2266                                                    const Latin1Char* s,
   2267                                                    size_t n,
   2268                                                    js::HashNumber hash);
   2269 
   2270 template <typename CharT>
   2271 JSAtom* NewAtomCopyNMaybeDeflateValidLength(JSContext* cx, const CharT* s,
   2272                                            size_t n, js::HashNumber hash) {
   2273  if constexpr (std::is_same_v<CharT, char16_t>) {
   2274    if (CanStoreCharsAsLatin1(s, n)) {
   2275      return NewAtomDeflatedValidLength(cx, s, n, hash);
   2276    }
   2277  }
   2278 
   2279  return NewAtomCopyNDontDeflateValidLength(cx, s, n, hash);
   2280 }
   2281 
   2282 template JSAtom* NewAtomCopyNMaybeDeflateValidLength(JSContext* cx,
   2283                                                     const char16_t* s,
   2284                                                     size_t n,
   2285                                                     js::HashNumber hash);
   2286 
   2287 template JSAtom* NewAtomCopyNMaybeDeflateValidLength(JSContext* cx,
   2288                                                     const Latin1Char* s,
   2289                                                     size_t n,
   2290                                                     js::HashNumber hash);
   2291 
   2292 JSLinearString* NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars& utf8,
   2293                                   JS::SmallestEncoding encoding,
   2294                                   gc::Heap heap) {
   2295  if (encoding == JS::SmallestEncoding::ASCII) {
   2296    return NewStringCopyN<js::CanGC>(cx, utf8.begin().get(), utf8.length(),
   2297                                     heap);
   2298  }
   2299 
   2300  size_t length;
   2301  if (encoding == JS::SmallestEncoding::Latin1) {
   2302    UniqueLatin1Chars latin1(
   2303        UTF8CharsToNewLatin1CharsZ(cx, utf8, &length, js::StringBufferArena)
   2304            .get());
   2305    if (!latin1) {
   2306      return nullptr;
   2307    }
   2308 
   2309    return NewString<js::CanGC>(cx, std::move(latin1), length, heap);
   2310  }
   2311 
   2312  MOZ_ASSERT(encoding == JS::SmallestEncoding::UTF16);
   2313 
   2314  UniqueTwoByteChars utf16(
   2315      UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length, js::StringBufferArena)
   2316          .get());
   2317  if (!utf16) {
   2318    return nullptr;
   2319  }
   2320 
   2321  return NewString<js::CanGC>(cx, std::move(utf16), length, heap);
   2322 }
   2323 
   2324 JSLinearString* NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars& utf8,
   2325                                   gc::Heap heap) {
   2326  JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
   2327  return NewStringCopyUTF8N(cx, utf8, encoding, heap);
   2328 }
   2329 
   2330 template <typename CharT>
   2331 MOZ_ALWAYS_INLINE JSLinearString* ExternalStringCache::lookupImpl(
   2332    const CharT* chars, size_t len) const {
   2333  AutoCheckCannotGC nogc;
   2334 
   2335  for (size_t i = 0; i < NumEntries; i++) {
   2336    JSLinearString* str = entries_[i];
   2337    if (!str || str->length() != len) {
   2338      continue;
   2339    }
   2340 
   2341    if constexpr (std::is_same_v<CharT, JS::Latin1Char>) {
   2342      if (!str->hasLatin1Chars()) {
   2343        continue;
   2344      }
   2345    } else {
   2346      if (!str->hasTwoByteChars()) {
   2347        continue;
   2348      }
   2349    }
   2350 
   2351    const CharT* strChars = str->chars<CharT>(nogc);
   2352    if (chars == strChars) {
   2353      // Note that we don't need an incremental barrier here or below.
   2354      // The cache is purged on GC so any string we get from the cache
   2355      // must have been allocated after the GC started.
   2356      MOZ_ASSERT(!str->isInline());
   2357      return str;
   2358    }
   2359 
   2360    // Compare the chars. Don't do this for long strings as it will be
   2361    // faster to allocate a new external string.
   2362    static const size_t MaxLengthForCharComparison = 100;
   2363    if (len <= MaxLengthForCharComparison && EqualChars(chars, strChars, len)) {
   2364      return str;
   2365    }
   2366  }
   2367 
   2368  return nullptr;
   2369 }
   2370 
   2371 MOZ_ALWAYS_INLINE JSLinearString* ExternalStringCache::lookup(
   2372    const JS::Latin1Char* chars, size_t len) const {
   2373  return lookupImpl(chars, len);
   2374 }
   2375 MOZ_ALWAYS_INLINE JSLinearString* ExternalStringCache::lookup(
   2376    const char16_t* chars, size_t len) const {
   2377  return lookupImpl(chars, len);
   2378 }
   2379 
   2380 MOZ_ALWAYS_INLINE void ExternalStringCache::put(JSLinearString* str) {
   2381  for (size_t i = NumEntries - 1; i > 0; i--) {
   2382    entries_[i] = entries_[i - 1];
   2383  }
   2384  entries_[0] = str;
   2385 }
   2386 
   2387 template <typename CharT>
   2388 MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInlineLatin1Impl(
   2389    const CharT* chars, size_t len) const {
   2390  MOZ_ASSERT(CanStoreCharsAsLatin1(chars, len));
   2391  MOZ_ASSERT(JSThinInlineString::lengthFits<Latin1Char>(len));
   2392 
   2393  AutoCheckCannotGC nogc;
   2394 
   2395  for (size_t i = 0; i < NumEntries; i++) {
   2396    JSInlineString* str = inlineLatin1Entries_[i];
   2397    if (!str || str->length() != len) {
   2398      continue;
   2399    }
   2400 
   2401    const JS::Latin1Char* strChars = str->latin1Chars(nogc);
   2402    if (EqualChars(chars, strChars, len)) {
   2403      return str;
   2404    }
   2405  }
   2406 
   2407  return nullptr;
   2408 }
   2409 
   2410 MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInlineLatin1(
   2411    const JS::Latin1Char* chars, size_t len) const {
   2412  return lookupInlineLatin1Impl(chars, len);
   2413 }
   2414 MOZ_ALWAYS_INLINE JSInlineString* ExternalStringCache::lookupInlineLatin1(
   2415    const char16_t* chars, size_t len) const {
   2416  return lookupInlineLatin1Impl(chars, len);
   2417 }
   2418 
   2419 MOZ_ALWAYS_INLINE void ExternalStringCache::putInlineLatin1(
   2420    JSInlineString* str) {
   2421  MOZ_ASSERT(str->hasLatin1Chars());
   2422 
   2423  for (size_t i = NumEntries - 1; i > 0; i--) {
   2424    inlineLatin1Entries_[i] = inlineLatin1Entries_[i - 1];
   2425  }
   2426  inlineLatin1Entries_[0] = str;
   2427 }
   2428 
   2429 } /* namespace js */
   2430 
   2431 template <AllowGC allowGC>
   2432 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineStringMaybeDeflated(
   2433    JSContext* cx, const mozilla::Range<const JS::Latin1Char>& chars,
   2434    gc::Heap heap = gc::Heap::Default) {
   2435  return NewInlineString<allowGC>(cx, chars, heap);
   2436 }
   2437 
   2438 template <AllowGC allowGC>
   2439 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineStringMaybeDeflated(
   2440    JSContext* cx, const mozilla::Range<const char16_t>& chars,
   2441    gc::Heap heap = gc::Heap::Default) {
   2442  return NewInlineStringDeflated<allowGC>(cx, chars, heap);
   2443 }
   2444 
   2445 namespace js {
   2446 
   2447 template <typename CharT>
   2448 JSString* NewMaybeExternalString(JSContext* cx, const CharT* s, size_t n,
   2449                                 const JSExternalStringCallbacks* callbacks,
   2450                                 bool* allocatedExternal, gc::Heap heap) {
   2451  if (JSString* str = TryEmptyOrStaticString(cx, s, n)) {
   2452    *allocatedExternal = false;
   2453    return str;
   2454  }
   2455 
   2456  ExternalStringCache& cache = cx->zone()->externalStringCache();
   2457 
   2458  if (JSThinInlineString::lengthFits<Latin1Char>(n) &&
   2459      CanStoreCharsAsLatin1(s, n)) {
   2460    *allocatedExternal = false;
   2461    if (JSInlineString* str = cache.lookupInlineLatin1(s, n)) {
   2462      return str;
   2463    }
   2464    JSInlineString* str = NewInlineStringMaybeDeflated<AllowGC::CanGC>(
   2465        cx, mozilla::Range<const CharT>(s, n), heap);
   2466    if (!str) {
   2467      return nullptr;
   2468    }
   2469    cache.putInlineLatin1(str);
   2470    return str;
   2471  }
   2472 
   2473  if (auto* str = cache.lookup(s, n)) {
   2474    *allocatedExternal = false;
   2475    return str;
   2476  }
   2477 
   2478  JSExternalString* str = JSExternalString::new_(cx, s, n, callbacks);
   2479  if (!str) {
   2480    return nullptr;
   2481  }
   2482 
   2483  *allocatedExternal = true;
   2484  cache.put(str);
   2485  return str;
   2486 }
   2487 
   2488 template JSString* NewMaybeExternalString(
   2489    JSContext* cx, const JS::Latin1Char* s, size_t n,
   2490    const JSExternalStringCallbacks* callbacks, bool* allocatedExternal,
   2491    gc::Heap heap);
   2492 
   2493 template JSString* NewMaybeExternalString(
   2494    JSContext* cx, const char16_t* s, size_t n,
   2495    const JSExternalStringCallbacks* callbacks, bool* allocatedExternal,
   2496    gc::Heap heap);
   2497 
   2498 } /* namespace js */
   2499 
   2500 template <typename CharT, typename BufferT>
   2501 static JSString* NewStringFromBuffer(JSContext* cx, BufferT&& buffer,
   2502                                     size_t length) {
   2503  AssertHeapIsIdle();
   2504  CHECK_THREAD(cx);
   2505 
   2506  const auto* s = static_cast<const CharT*>(buffer->Data());
   2507 
   2508  if (JSString* str = TryEmptyOrStaticString(cx, s, length)) {
   2509    return str;
   2510  }
   2511 
   2512  ExternalStringCache& cache = cx->zone()->externalStringCache();
   2513 
   2514  // Use the inline-string cache that we also use for external strings.
   2515  if (JSThinInlineString::lengthFits<Latin1Char>(length) &&
   2516      CanStoreCharsAsLatin1(s, length)) {
   2517    if (JSInlineString* str = cache.lookupInlineLatin1(s, length)) {
   2518      return str;
   2519    }
   2520    JSInlineString* str = NewInlineStringMaybeDeflated<AllowGC::CanGC>(
   2521        cx, mozilla::Range(s, length), gc::Heap::Default);
   2522    if (!str) {
   2523      return nullptr;
   2524    }
   2525    cache.putInlineLatin1(str);
   2526    return str;
   2527  }
   2528 
   2529  if (auto* str = cache.lookup(s, length)) {
   2530    return str;
   2531  }
   2532 
   2533  JSLinearString* str;
   2534  if (JSInlineString::lengthFits<CharT>(length)) {
   2535    str = NewInlineString<CanGC>(cx, mozilla::Range(s, length),
   2536                                 gc::Heap::Default);
   2537  } else {
   2538    // Note: |buffer| is either a StringBuffer* or a RefPtr<StringBuffer>, so
   2539    // ensure we have a RefPtr.
   2540    RefPtr<mozilla::StringBuffer> bufferRef(std::move(buffer));
   2541    Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(bufferRef), length);
   2542    str = JSLinearString::new_<CanGC, CharT>(cx, &owned, gc::Heap::Default);
   2543  }
   2544  if (!str) {
   2545    return nullptr;
   2546  }
   2547  cache.put(str);
   2548  return str;
   2549 }
   2550 
   2551 JS_PUBLIC_API JSString* JS::NewStringFromLatin1Buffer(
   2552    JSContext* cx, RefPtr<mozilla::StringBuffer> buffer, size_t length) {
   2553  return NewStringFromBuffer<Latin1Char>(cx, std::move(buffer), length);
   2554 }
   2555 
   2556 JS_PUBLIC_API JSString* JS::NewStringFromKnownLiveLatin1Buffer(
   2557    JSContext* cx, mozilla::StringBuffer* buffer, size_t length) {
   2558  return NewStringFromBuffer<Latin1Char>(cx, buffer, length);
   2559 }
   2560 
   2561 JS_PUBLIC_API JSString* JS::NewStringFromTwoByteBuffer(
   2562    JSContext* cx, RefPtr<mozilla::StringBuffer> buffer, size_t length) {
   2563  return NewStringFromBuffer<char16_t>(cx, std::move(buffer), length);
   2564 }
   2565 
   2566 JS_PUBLIC_API JSString* JS::NewStringFromKnownLiveTwoByteBuffer(
   2567    JSContext* cx, mozilla::StringBuffer* buffer, size_t length) {
   2568  return NewStringFromBuffer<char16_t>(cx, buffer, length);
   2569 }
   2570 
   2571 template <typename BufferT>
   2572 static JSString* NewStringFromUTF8Buffer(JSContext* cx, BufferT&& buffer,
   2573                                         size_t length) {
   2574  AssertHeapIsIdle();
   2575  CHECK_THREAD(cx);
   2576 
   2577  const JS::UTF8Chars utf8(static_cast<const char*>(buffer->Data()), length);
   2578 
   2579  JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
   2580  if (encoding == JS::SmallestEncoding::ASCII) {
   2581    // ASCII case can use the string buffer as Latin1 buffer.
   2582    return NewStringFromBuffer<Latin1Char>(cx, std::move(buffer), length);
   2583  }
   2584 
   2585  // Non-ASCII case cannot use the string buffer.
   2586  return NewStringCopyUTF8N(cx, utf8, encoding);
   2587 }
   2588 
   2589 JS_PUBLIC_API JSString* JS::NewStringFromUTF8Buffer(
   2590    JSContext* cx, RefPtr<mozilla::StringBuffer> buffer, size_t length) {
   2591  return ::NewStringFromUTF8Buffer(cx, std::move(buffer), length);
   2592 }
   2593 
   2594 JS_PUBLIC_API JSString* JS::NewStringFromKnownLiveUTF8Buffer(
   2595    JSContext* cx, mozilla::StringBuffer* buffer, size_t length) {
   2596  return ::NewStringFromUTF8Buffer(cx, buffer, length);
   2597 }
   2598 
   2599 #if defined(DEBUG) || defined(JS_JITSPEW) || defined(JS_CACHEIR_SPEW)
   2600 void JSExtensibleString::dumpOwnRepresentationFields(
   2601    js::JSONPrinter& json) const {
   2602  json.property("capacity", capacity());
   2603 }
   2604 
   2605 void JSInlineString::dumpOwnRepresentationFields(js::JSONPrinter& json) const {}
   2606 
   2607 void JSLinearString::dumpOwnRepresentationFields(js::JSONPrinter& json) const {
   2608  if (hasStringBuffer()) {
   2609 #  ifdef DEBUG
   2610    json.property("bufferRefCount", stringBuffer()->RefCount());
   2611 #  endif
   2612    return;
   2613  }
   2614  if (!isInline()) {
   2615    // Include whether the chars are in the nursery even for tenured
   2616    // strings, which should always be false. For investigating bugs, it's
   2617    // better to not assume that.
   2618    js::Nursery& nursery = runtimeFromMainThread()->gc.nursery();
   2619    bool inNursery = nursery.isInside(nonInlineCharsRaw());
   2620    json.boolProperty("charsInNursery", inNursery);
   2621  }
   2622 }
   2623 #endif
   2624 
   2625 struct RepresentativeExternalString : public JSExternalStringCallbacks {
   2626  void finalize(JS::Latin1Char* chars) const override {
   2627    // Constant chars, nothing to do.
   2628  }
   2629  void finalize(char16_t* chars) const override {
   2630    // Constant chars, nothing to do.
   2631  }
   2632  size_t sizeOfBuffer(const JS::Latin1Char* chars,
   2633                      mozilla::MallocSizeOf mallocSizeOf) const override {
   2634    // This string's buffer is not heap-allocated, so its malloc size is 0.
   2635    return 0;
   2636  }
   2637  size_t sizeOfBuffer(const char16_t* chars,
   2638                      mozilla::MallocSizeOf mallocSizeOf) const override {
   2639    // This string's buffer is not heap-allocated, so its malloc size is 0.
   2640    return 0;
   2641  }
   2642 };
   2643 
   2644 static const RepresentativeExternalString RepresentativeExternalStringCallbacks;
   2645 
   2646 template <typename CheckString, typename CharT>
   2647 static bool FillWithRepresentatives(JSContext* cx, Handle<ArrayObject*> array,
   2648                                    uint32_t* index, const CharT* chars,
   2649                                    size_t len, size_t inlineStringMaxLength,
   2650                                    size_t inlineAtomMaxLength,
   2651                                    const CheckString& check, gc::Heap heap) {
   2652  auto AppendString = [&check](JSContext* cx, Handle<ArrayObject*> array,
   2653                               uint32_t* index, HandleString s) {
   2654    MOZ_ASSERT(check(s));
   2655    (void)check;  // silence clang -Wunused-lambda-capture in opt builds
   2656    RootedValue val(cx, StringValue(s));
   2657    return JS_DefineElement(cx, array, (*index)++, val, 0);
   2658  };
   2659 
   2660  MOZ_ASSERT(len > inlineStringMaxLength);
   2661  MOZ_ASSERT(len > inlineAtomMaxLength);
   2662 
   2663  // Normal atom.
   2664  RootedString atom1(cx, AtomizeChars(cx, chars, len));
   2665  if (!atom1 || !AppendString(cx, array, index, atom1)) {
   2666    return false;
   2667  }
   2668  MOZ_ASSERT(atom1->isAtom());
   2669 
   2670  // Thin inline atom.
   2671  RootedString atom2(cx, AtomizeChars(cx, chars, 2));
   2672  if (!atom2 || !AppendString(cx, array, index, atom2)) {
   2673    return false;
   2674  }
   2675  MOZ_ASSERT(atom2->isAtom());
   2676  MOZ_ASSERT(atom2->isInline());
   2677 
   2678  // Fat inline atom.
   2679  RootedString atom3(cx, AtomizeChars(cx, chars, inlineAtomMaxLength));
   2680  if (!atom3 || !AppendString(cx, array, index, atom3)) {
   2681    return false;
   2682  }
   2683  MOZ_ASSERT(atom3->isAtom());
   2684  MOZ_ASSERT_IF(inlineStringMaxLength < inlineAtomMaxLength,
   2685                atom3->isFatInline());
   2686 
   2687  // Normal linear string; maybe nursery.
   2688  RootedString linear1(cx, NewStringCopyN<CanGC>(cx, chars, len, heap));
   2689  if (!linear1 || !AppendString(cx, array, index, linear1)) {
   2690    return false;
   2691  }
   2692  MOZ_ASSERT(linear1->isLinear());
   2693 
   2694  // Inline string; maybe nursery.
   2695  RootedString linear2(cx, NewStringCopyN<CanGC>(cx, chars, 3, heap));
   2696  if (!linear2 || !AppendString(cx, array, index, linear2)) {
   2697    return false;
   2698  }
   2699  MOZ_ASSERT(linear2->isLinear());
   2700  MOZ_ASSERT(linear2->isInline());
   2701 
   2702  // Fat inline string; maybe nursery.
   2703  RootedString linear3(
   2704      cx, NewStringCopyN<CanGC>(cx, chars, inlineStringMaxLength, heap));
   2705  if (!linear3 || !AppendString(cx, array, index, linear3)) {
   2706    return false;
   2707  }
   2708  MOZ_ASSERT(linear3->isLinear());
   2709  MOZ_ASSERT(linear3->isFatInline());
   2710 
   2711  // Rope; maybe nursery.
   2712  RootedString rope(cx, ConcatStrings<CanGC>(cx, atom1, atom3, heap));
   2713  if (!rope || !AppendString(cx, array, index, rope)) {
   2714    return false;
   2715  }
   2716  MOZ_ASSERT(rope->isRope());
   2717 
   2718  // Dependent; maybe nursery.
   2719  RootedString dep(cx, NewDependentString(cx, atom1, 0, len - 2, heap));
   2720  if (!dep || !AppendString(cx, array, index, dep)) {
   2721    return false;
   2722  }
   2723  MOZ_ASSERT(dep->isDependent());
   2724 
   2725  // Extensible; maybe nursery.
   2726  RootedString temp1(cx, NewStringCopyN<CanGC>(cx, chars, len, heap));
   2727  if (!temp1) {
   2728    return false;
   2729  }
   2730  RootedString extensible(cx, ConcatStrings<CanGC>(cx, temp1, atom3, heap));
   2731  if (!extensible || !extensible->ensureLinear(cx)) {
   2732    return false;
   2733  }
   2734  if (!AppendString(cx, array, index, extensible)) {
   2735    return false;
   2736  }
   2737  MOZ_ASSERT(extensible->isExtensible());
   2738 
   2739  RootedString external1(cx), external2(cx);
   2740  if constexpr (std::is_same_v<CharT, char16_t>) {
   2741    external1 = JS_NewExternalUCString(cx, (const char16_t*)chars, len,
   2742                                       &RepresentativeExternalStringCallbacks);
   2743    if (!external1 || !AppendString(cx, array, index, external1)) {
   2744      return false;
   2745    }
   2746    MOZ_ASSERT(external1->isExternal());
   2747 
   2748    external2 = JS_NewExternalUCString(cx, (const char16_t*)chars, 2,
   2749                                       &RepresentativeExternalStringCallbacks);
   2750    if (!external2 || !AppendString(cx, array, index, external2)) {
   2751      return false;
   2752    }
   2753    MOZ_ASSERT(external2->isExternal());
   2754  } else {
   2755    external1 =
   2756        JS_NewExternalStringLatin1(cx, (const Latin1Char*)chars, len,
   2757                                   &RepresentativeExternalStringCallbacks);
   2758    if (!external1 || !AppendString(cx, array, index, external1)) {
   2759      return false;
   2760    }
   2761    MOZ_ASSERT(external1->isExternal());
   2762 
   2763    external2 =
   2764        JS_NewExternalStringLatin1(cx, (const Latin1Char*)chars, 2,
   2765                                   &RepresentativeExternalStringCallbacks);
   2766    if (!external2 || !AppendString(cx, array, index, external2)) {
   2767      return false;
   2768    }
   2769    MOZ_ASSERT(external2->isExternal());
   2770  }
   2771 
   2772  // Assert the strings still have the types we expect after creating the
   2773  // other strings.
   2774 
   2775  MOZ_ASSERT(atom1->isAtom());
   2776  MOZ_ASSERT(atom2->isAtom());
   2777  MOZ_ASSERT(atom3->isAtom());
   2778  MOZ_ASSERT(atom2->isInline());
   2779  MOZ_ASSERT_IF(inlineStringMaxLength < inlineAtomMaxLength,
   2780                atom3->isFatInline());
   2781 
   2782  MOZ_ASSERT(linear1->isLinear());
   2783  MOZ_ASSERT(linear2->isLinear());
   2784  MOZ_ASSERT(linear3->isLinear());
   2785  MOZ_ASSERT(linear2->isInline());
   2786  MOZ_ASSERT(linear3->isFatInline());
   2787 
   2788  MOZ_ASSERT(rope->isRope());
   2789  MOZ_ASSERT(dep->isDependent());
   2790  MOZ_ASSERT(extensible->isExtensible());
   2791  MOZ_ASSERT(external1->isExternal());
   2792  MOZ_ASSERT(external2->isExternal());
   2793  return true;
   2794 }
   2795 
   2796 /* static */
   2797 bool JSString::fillWithRepresentatives(JSContext* cx,
   2798                                       Handle<ArrayObject*> array) {
   2799  uint32_t index = 0;
   2800 
   2801  auto CheckTwoByte = [](JSString* str) { return str->hasTwoByteChars(); };
   2802  auto CheckLatin1 = [](JSString* str) { return str->hasLatin1Chars(); };
   2803 
   2804  static const char16_t twoByteChars[] =
   2805      u"\u1234abc\0def\u5678ghijklmasdfa\0xyz0123456789";
   2806  static const Latin1Char latin1Chars[] = "abc\0defghijklmasdfa\0xyz0123456789";
   2807 
   2808  // Create strings using both the default heap and forcing the tenured heap. If
   2809  // nursery strings are available, this is a best effort at creating them in
   2810  // the default heap case. Since nursery strings may be disabled or a GC may
   2811  // occur during this process, there may be duplicate representatives in the
   2812  // final list.
   2813 
   2814  if (!FillWithRepresentatives(cx, array, &index, twoByteChars,
   2815                               std::size(twoByteChars) - 1,
   2816                               JSFatInlineString::MAX_LENGTH_TWO_BYTE,
   2817                               js::FatInlineAtom::MAX_LENGTH_TWO_BYTE,
   2818                               CheckTwoByte, gc::Heap::Tenured)) {
   2819    return false;
   2820  }
   2821  if (!FillWithRepresentatives(cx, array, &index, latin1Chars,
   2822                               std::size(latin1Chars) - 1,
   2823                               JSFatInlineString::MAX_LENGTH_LATIN1,
   2824                               js::FatInlineAtom::MAX_LENGTH_LATIN1,
   2825                               CheckLatin1, gc::Heap::Tenured)) {
   2826    return false;
   2827  }
   2828  if (!FillWithRepresentatives(cx, array, &index, twoByteChars,
   2829                               std::size(twoByteChars) - 1,
   2830                               JSFatInlineString::MAX_LENGTH_TWO_BYTE,
   2831                               js::FatInlineAtom::MAX_LENGTH_TWO_BYTE,
   2832                               CheckTwoByte, gc::Heap::Default)) {
   2833    return false;
   2834  }
   2835  if (!FillWithRepresentatives(cx, array, &index, latin1Chars,
   2836                               std::size(latin1Chars) - 1,
   2837                               JSFatInlineString::MAX_LENGTH_LATIN1,
   2838                               js::FatInlineAtom::MAX_LENGTH_LATIN1,
   2839                               CheckLatin1, gc::Heap::Default)) {
   2840    return false;
   2841  }
   2842 
   2843 #ifdef DEBUG
   2844  //  * Normal atom
   2845  //  * Inline atom.
   2846  //  * Fat inline atom.
   2847  //  * Normal linear string
   2848  //  * Inline string
   2849  //  * Fat inline string
   2850  //  * Rope; maybe nursery.
   2851  //  * Dependent
   2852  //  * Extensible
   2853  //  * External with original len
   2854  //  * External with len==2
   2855  static constexpr uint32_t StringTypes = 11;
   2856  //  * Latin1
   2857  //  * TwoByte
   2858  static constexpr uint32_t CharTypes = 2;
   2859  //  * Tenured
   2860  //  * Default
   2861  static constexpr uint32_t HeapType = 2;
   2862  MOZ_ASSERT(index == StringTypes * CharTypes * HeapType);
   2863 #endif
   2864 
   2865  return true;
   2866 }
   2867 
   2868 bool JSString::tryReplaceWithAtomRef(JSAtom* atom) {
   2869  MOZ_ASSERT(!isAtomRef());
   2870 
   2871  if (isDependedOn() || isInline() || isExternal()) {
   2872    return false;
   2873  }
   2874 
   2875  AutoCheckCannotGC nogc;
   2876  if (hasOutOfLineChars()) {
   2877    if (asLinear().hasStringBuffer()) {
   2878      // If the string is in the nursery, the reference to the buffer will be
   2879      // released during the next minor GC (in Nursery::sweep). If the string is
   2880      // tenured, we have to release this reference here.
   2881      if (isTenured()) {
   2882        RemoveCellMemory(this, allocSize(), MemoryUse::StringContents);
   2883        asLinear().stringBuffer()->Release();
   2884      }
   2885    } else {
   2886      void* buffer = asLinear().nonInlineCharsRaw();
   2887      // This is a little cheeky and so deserves a comment. If the string is
   2888      // not tenured, then either its buffer lives purely in the nursery, in
   2889      // which case it will just be forgotten and blown away in the next
   2890      // minor GC, or it is tracked in the nursery's mallocedBuffers hashtable,
   2891      // in which case it will be freed for us in the next minor GC. We opt
   2892      // to let the GC take care of it since there's a chance it will run
   2893      // during idle time.
   2894      if (isTenured()) {
   2895        RemoveCellMemory(this, allocSize(), MemoryUse::StringContents);
   2896        js_free(buffer);
   2897      }
   2898    }
   2899  }
   2900 
   2901  // Pre-barrier for d.s.u3 which is overwritten and d.s.u2 which is ignored
   2902  // for atom refs.
   2903  MOZ_ASSERT(isRope() || isLinear());
   2904  if (isRope()) {
   2905    PreWriteBarrier(d.s.u2.left);
   2906    PreWriteBarrier(d.s.u3.right);
   2907  } else if (isDependent()) {
   2908    PreWriteBarrier(d.s.u3.base);
   2909  }
   2910 
   2911  uint32_t flags = INIT_ATOM_REF_FLAGS;
   2912  d.s.u3.atom = atom;
   2913  if (atom->hasLatin1Chars()) {
   2914    flags |= LATIN1_CHARS_BIT;
   2915    setLengthAndFlags(length(), flags);
   2916    setNonInlineChars(atom->chars<Latin1Char>(nogc), atom->hasStringBuffer());
   2917  } else {
   2918    setLengthAndFlags(length(), flags);
   2919    setNonInlineChars(atom->chars<char16_t>(nogc), atom->hasStringBuffer());
   2920  }
   2921  // Redundant, but just a reminder that this needs to be true or else we need
   2922  // to check and conditionally put ourselves in the store buffer
   2923  MOZ_ASSERT(atom->isTenured());
   2924  return true;
   2925 }
   2926 
   2927 template <typename CharT>
   2928 bool js::StringChars<CharT>::maybeAlloc(JSContext* cx, size_t length,
   2929                                        gc::Heap heap) {
   2930  assertValidRequest(0, length);
   2931 
   2932  if (JSInlineString::lengthFits<CharT>(length)) {
   2933    return true;
   2934  }
   2935 
   2936  if (MOZ_UNLIKELY(!JSString::validateLength(cx, length))) {
   2937    return false;
   2938  }
   2939 
   2940  auto chars = AllocChars<CharT>(cx, length, heap);
   2941  if (!chars) {
   2942    return false;
   2943  }
   2944 
   2945  ownedChars_.set(std::move(chars));
   2946  return true;
   2947 }
   2948 
   2949 template bool js::StringChars<JS::Latin1Char>::maybeAlloc(JSContext*, size_t,
   2950                                                          gc::Heap);
   2951 template bool js::StringChars<char16_t>::maybeAlloc(JSContext*, size_t,
   2952                                                    gc::Heap);
   2953 
   2954 template <typename CharT>
   2955 bool js::StringChars<CharT>::maybeRealloc(JSContext* cx, size_t oldLength,
   2956                                          size_t newLength, gc::Heap heap) {
   2957  assertValidRequest(oldLength, newLength);
   2958 
   2959  // Nothing to do if new length still fits into inline storage.
   2960  if (JSInlineString::lengthFits<CharT>(newLength)) {
   2961    return true;
   2962  }
   2963 
   2964  if (MOZ_UNLIKELY(!JSString::validateLength(cx, newLength))) {
   2965    return false;
   2966  }
   2967 
   2968  // Unused |ownedChars_| means we were previously using inlining storage.
   2969  if (!ownedChars_) {
   2970    auto chars = AllocChars<CharT>(cx, newLength, heap);
   2971    if (!chars) {
   2972      return false;
   2973    }
   2974    std::memcpy(chars.data(), inlineChars_, InlineLength * sizeof(CharT));
   2975 
   2976    ownedChars_.set(std::move(chars));
   2977    return true;
   2978  }
   2979 
   2980  // Use |realloc| for malloc'ed characters.
   2981  if (ownedChars_.isMalloced()) {
   2982    CharT* oldChars = ownedChars_.release();
   2983    CharT* newChars = cx->pod_arena_realloc(js::StringBufferArena, oldChars,
   2984                                            oldLength, newLength);
   2985    if (!newChars) {
   2986      js_free(oldChars);
   2987      return false;
   2988    }
   2989 
   2990    using Kind = typename JSString::OwnedChars<CharT>::Kind;
   2991    ownedChars_.set({newChars, newLength, Kind::Malloc});
   2992    return true;
   2993  }
   2994 
   2995  // Use |StringBuffer::Realloc| for string buffers.
   2996  if (ownedChars_.hasStringBuffer()) {
   2997    static_assert(
   2998        mozilla::StringBuffer::IsValidLength<CharT>(JSString::MAX_LENGTH),
   2999        "JSString length must be valid for StringBuffer");
   3000 
   3001    auto* oldBuffer = mozilla::StringBuffer::FromData(ownedChars_.release());
   3002 
   3003    // Note: StringBuffers must be null-terminated.
   3004    auto* newBuffer = mozilla::StringBuffer::Realloc(
   3005        oldBuffer, (newLength + 1) * sizeof(CharT),
   3006        mozilla::Some(js::StringBufferArena));
   3007    if (!newBuffer) {
   3008      oldBuffer->Release();
   3009      ReportOutOfMemory(cx);
   3010      return false;
   3011    }
   3012    auto* newChars = static_cast<CharT*>(newBuffer->Data());
   3013    newChars[newLength] = '\0';
   3014 
   3015    using Kind = typename JSString::OwnedChars<CharT>::Kind;
   3016    ownedChars_.set({newChars, newLength, Kind::StringBuffer});
   3017 
   3018    MOZ_ASSERT(newBuffer->RefCount() == 1);
   3019    return true;
   3020  }
   3021 
   3022  // Keep the previous nursery allocated chars alive.
   3023  Rooted<JSString::OwnedChars<CharT>> oldOwnedChars(
   3024      cx, std::move(ownedChars_.get()));
   3025 
   3026  // Nursery allocated characters can't be reallocated, so perform a new
   3027  // allocation instead.
   3028  auto chars = AllocChars<CharT>(cx, newLength, heap);
   3029  if (!chars) {
   3030    return false;
   3031  }
   3032  mozilla::PodCopy(chars.data(), oldOwnedChars.data(), oldLength);
   3033 
   3034  ownedChars_.set(std::move(chars));
   3035  return true;
   3036 }
   3037 
   3038 template bool js::StringChars<JS::Latin1Char>::maybeRealloc(JSContext*, size_t,
   3039                                                            size_t, gc::Heap);
   3040 template bool js::StringChars<char16_t>::maybeRealloc(JSContext*, size_t,
   3041                                                      size_t, gc::Heap);
   3042 
   3043 template <typename CharT>
   3044 template <AllowGC allowGC>
   3045 JSLinearString* js::StringChars<CharT>::toStringDontDeflate(JSContext* cx,
   3046                                                            size_t length,
   3047                                                            gc::Heap heap) {
   3048  MOZ_ASSERT(length == lastRequestedLength_);
   3049 
   3050  if (JSInlineString::lengthFits<CharT>(length)) {
   3051    MOZ_ASSERT(!ownedChars_,
   3052               "unexpected OwnedChars allocation for inline strings");
   3053    if (auto* str = TryEmptyOrStaticString(cx, inlineChars_, length)) {
   3054      return str;
   3055    }
   3056    return NewInlineString<CanGC>(cx, inlineChars_, length, heap);
   3057  }
   3058 
   3059  MOZ_ASSERT(ownedChars_,
   3060             "missing OwnedChars allocation for non-inline strings");
   3061  MOZ_ASSERT(length == ownedChars_.length(),
   3062             "requested length doesn't match allocation");
   3063  return JSLinearString::newValidLength<allowGC, CharT>(cx, &ownedChars_, heap);
   3064 }
   3065 
   3066 template JSLinearString*
   3067 js::StringChars<JS::Latin1Char>::toStringDontDeflate<CanGC>(JSContext*, size_t,
   3068                                                            gc::Heap);
   3069 template JSLinearString* js::StringChars<char16_t>::toStringDontDeflate<CanGC>(
   3070    JSContext*, size_t, gc::Heap);
   3071 template JSLinearString*
   3072 js::StringChars<JS::Latin1Char>::toStringDontDeflate<NoGC>(JSContext*, size_t,
   3073                                                           gc::Heap);
   3074 template JSLinearString* js::StringChars<char16_t>::toStringDontDeflate<NoGC>(
   3075    JSContext*, size_t, gc::Heap);
   3076 
   3077 template <typename CharT>
   3078 template <AllowGC allowGC>
   3079 JSLinearString* js::StringChars<CharT>::toStringDontDeflateNonStatic(
   3080    JSContext* cx, size_t length, gc::Heap heap) {
   3081  MOZ_ASSERT(length == lastRequestedLength_);
   3082 
   3083  if (JSInlineString::lengthFits<CharT>(length)) {
   3084    MOZ_ASSERT(!ownedChars_,
   3085               "unexpected OwnedChars allocation for inline strings");
   3086    MOZ_ASSERT(!TryEmptyOrStaticString(cx, inlineChars_, length),
   3087               "unexpected static string found");
   3088    return NewInlineString<allowGC>(cx, inlineChars_, length, heap);
   3089  }
   3090 
   3091  MOZ_ASSERT(ownedChars_,
   3092             "missing OwnedChars allocation for non-inline strings");
   3093  MOZ_ASSERT(length == ownedChars_.length(),
   3094             "requested length doesn't match allocation");
   3095  return JSLinearString::newValidLength<allowGC, CharT>(cx, &ownedChars_, heap);
   3096 }
   3097 
   3098 template JSLinearString*
   3099 js::StringChars<JS::Latin1Char>::toStringDontDeflateNonStatic<CanGC>(JSContext*,
   3100                                                                     size_t,
   3101                                                                     gc::Heap);
   3102 template JSLinearString*
   3103 js::StringChars<char16_t>::toStringDontDeflateNonStatic<CanGC>(JSContext*,
   3104                                                               size_t,
   3105                                                               gc::Heap);
   3106 template JSLinearString*
   3107 js::StringChars<JS::Latin1Char>::toStringDontDeflateNonStatic<NoGC>(JSContext*,
   3108                                                                    size_t,
   3109                                                                    gc::Heap);
   3110 template JSLinearString*
   3111 js::StringChars<char16_t>::toStringDontDeflateNonStatic<NoGC>(JSContext*,
   3112                                                              size_t, gc::Heap);
   3113 
   3114 template <typename CharT>
   3115 bool js::AtomStringChars<CharT>::maybeAlloc(JSContext* cx, size_t length) {
   3116  assertValidRequest(0, length);
   3117 
   3118  if (JSInlineString::lengthFits<CharT>(length)) {
   3119    return true;
   3120  }
   3121 
   3122  if (MOZ_UNLIKELY(!JSString::validateLength(cx, length))) {
   3123    return false;
   3124  }
   3125 
   3126  mallocChars_ = cx->make_pod_arena_array<CharT>(js::StringBufferArena, length);
   3127  return !!mallocChars_;
   3128 }
   3129 
   3130 template bool js::AtomStringChars<JS::Latin1Char>::maybeAlloc(JSContext*,
   3131                                                              size_t);
   3132 template bool js::AtomStringChars<char16_t>::maybeAlloc(JSContext*, size_t);
   3133 
   3134 template <typename CharT>
   3135 JSAtom* js::AtomStringChars<CharT>::toAtom(JSContext* cx, size_t length) {
   3136  MOZ_ASSERT(length == lastRequestedLength_);
   3137  return AtomizeChars(cx, data(), length);
   3138 }
   3139 
   3140 template JSAtom* js::AtomStringChars<JS::Latin1Char>::toAtom(JSContext*,
   3141                                                             size_t);
   3142 template JSAtom* js::AtomStringChars<char16_t>::toAtom(JSContext*, size_t);
   3143 
   3144 /*** Conversions ************************************************************/
   3145 
   3146 UniqueChars js::EncodeLatin1(JSContext* cx, JSString* str) {
   3147  JSLinearString* linear = str->ensureLinear(cx);
   3148  if (!linear) {
   3149    return nullptr;
   3150  }
   3151 
   3152  JS::AutoCheckCannotGC nogc;
   3153  if (linear->hasTwoByteChars()) {
   3154    JS::Latin1CharsZ chars =
   3155        JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, linear->twoByteRange(nogc));
   3156    return UniqueChars(chars.c_str());
   3157  }
   3158 
   3159  size_t len = str->length();
   3160  Latin1Char* buf = cx->pod_malloc<Latin1Char>(len + 1);
   3161  if (!buf) {
   3162    return nullptr;
   3163  }
   3164 
   3165  PodCopy(buf, linear->latin1Chars(nogc), len);
   3166  buf[len] = '\0';
   3167 
   3168  return UniqueChars(reinterpret_cast<char*>(buf));
   3169 }
   3170 
   3171 UniqueChars js::EncodeAscii(JSContext* cx, JSString* str) {
   3172  JSLinearString* linear = str->ensureLinear(cx);
   3173  if (!linear) {
   3174    return nullptr;
   3175  }
   3176 
   3177  MOZ_ASSERT(StringIsAscii(linear));
   3178  return EncodeLatin1(cx, linear);
   3179 }
   3180 
   3181 UniqueChars js::IdToPrintableUTF8(JSContext* cx, HandleId id,
   3182                                  IdToPrintableBehavior behavior) {
   3183  // ToString(<symbol>) throws a TypeError, therefore require that callers
   3184  // request source representation when |id| is a property key.
   3185  MOZ_ASSERT_IF(behavior == IdToPrintableBehavior::IdIsIdentifier,
   3186                id.isAtom() && IsIdentifierNameOrPrivateName(id.toAtom()));
   3187 
   3188  RootedValue v(cx, IdToValue(id));
   3189  JSString* str;
   3190  if (behavior == IdToPrintableBehavior::IdIsPropertyKey) {
   3191    str = ValueToSource(cx, v);
   3192  } else {
   3193    str = ToString<CanGC>(cx, v);
   3194  }
   3195  if (!str) {
   3196    return nullptr;
   3197  }
   3198  return StringToNewUTF8CharsZ(cx, *str);
   3199 }
   3200 
   3201 template <AllowGC allowGC>
   3202 JSString* js::ToStringSlow(
   3203    JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg) {
   3204  /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
   3205  MOZ_ASSERT(!arg.isString());
   3206 
   3207  Value v = arg;
   3208  if (!v.isPrimitive()) {
   3209    if (!allowGC) {
   3210      return nullptr;
   3211    }
   3212    RootedValue v2(cx, v);
   3213    if (!ToPrimitive(cx, JSTYPE_STRING, &v2)) {
   3214      return nullptr;
   3215    }
   3216    v = v2;
   3217  }
   3218 
   3219  JSString* str;
   3220  if (v.isString()) {
   3221    str = v.toString();
   3222  } else if (v.isInt32()) {
   3223    str = Int32ToString<allowGC>(cx, v.toInt32());
   3224  } else if (v.isDouble()) {
   3225    str = NumberToString<allowGC>(cx, v.toDouble());
   3226  } else if (v.isBoolean()) {
   3227    str = BooleanToString(cx, v.toBoolean());
   3228  } else if (v.isNull()) {
   3229    str = cx->names().null;
   3230  } else if (v.isSymbol()) {
   3231    if (allowGC) {
   3232      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   3233                                JSMSG_SYMBOL_TO_STRING);
   3234    }
   3235    return nullptr;
   3236  } else if (v.isBigInt()) {
   3237    if (!allowGC) {
   3238      return nullptr;
   3239    }
   3240    RootedBigInt i(cx, v.toBigInt());
   3241    str = BigInt::toString<CanGC>(cx, i, 10);
   3242  } else {
   3243    MOZ_ASSERT(v.isUndefined());
   3244    str = cx->names().undefined;
   3245  }
   3246  return str;
   3247 }
   3248 
   3249 template JSString* js::ToStringSlow<CanGC>(JSContext* cx, HandleValue arg);
   3250 
   3251 template JSString* js::ToStringSlow<NoGC>(JSContext* cx, const Value& arg);
   3252 
   3253 JS_PUBLIC_API JSString* js::ToStringSlow(JSContext* cx, HandleValue v) {
   3254  return ToStringSlow<CanGC>(cx, v);
   3255 }