LCOV - code coverage report
Current view: top level - js/src/vm - String.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 427 802 53.2 %
Date: 2017-07-14 16:53:18 Functions: 54 101 53.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       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/String-inl.h"
       8             : 
       9             : #include "mozilla/MathAlgorithms.h"
      10             : #include "mozilla/MemoryReporting.h"
      11             : #include "mozilla/PodOperations.h"
      12             : #include "mozilla/RangedPtr.h"
      13             : #include "mozilla/SizePrintfMacros.h"
      14             : #include "mozilla/TypeTraits.h"
      15             : #include "mozilla/Unused.h"
      16             : 
      17             : #include "gc/Marking.h"
      18             : #include "js/GCAPI.h"
      19             : #include "js/UbiNode.h"
      20             : #include "vm/GeckoProfiler.h"
      21             : 
      22             : #include "jscntxtinlines.h"
      23             : #include "jscompartmentinlines.h"
      24             : 
      25             : using namespace js;
      26             : 
      27             : using mozilla::IsSame;
      28             : using mozilla::PodCopy;
      29             : using mozilla::PodEqual;
      30             : using mozilla::RangedPtr;
      31             : using mozilla::RoundUpPow2;
      32             : using mozilla::Unused;
      33             : 
      34             : using JS::AutoCheckCannotGC;
      35             : 
      36             : size_t
      37           0 : JSString::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
      38             : {
      39             :     // JSRope: do nothing, we'll count all children chars when we hit the leaf strings.
      40           0 :     if (isRope())
      41           0 :         return 0;
      42             : 
      43           0 :     MOZ_ASSERT(isLinear());
      44             : 
      45             :     // JSDependentString: do nothing, we'll count the chars when we hit the base string.
      46           0 :     if (isDependent())
      47           0 :         return 0;
      48             : 
      49             :     // JSExternalString: Ask the embedding to tell us what's going on.  If it
      50             :     // doesn't want to say, don't count, the chars could be stored anywhere.
      51           0 :     if (isExternal()) {
      52           0 :         if (auto* cb = runtimeFromActiveCooperatingThread()->externalStringSizeofCallback.ref()) {
      53             :             // Our callback isn't supposed to cause GC.
      54           0 :             JS::AutoSuppressGCAnalysis nogc;
      55           0 :             return cb(this, mallocSizeOf);
      56             :         }
      57           0 :         return 0;
      58             :     }
      59             : 
      60           0 :     MOZ_ASSERT(isFlat());
      61             : 
      62             :     // JSExtensibleString: count the full capacity, not just the used space.
      63           0 :     if (isExtensible()) {
      64           0 :         JSExtensibleString& extensible = asExtensible();
      65           0 :         return extensible.hasLatin1Chars()
      66           0 :                ? mallocSizeOf(extensible.rawLatin1Chars())
      67           0 :                : mallocSizeOf(extensible.rawTwoByteChars());
      68             :     }
      69             : 
      70             :     // JSInlineString, JSFatInlineString [JSInlineAtom, JSFatInlineAtom]: the chars are inline.
      71           0 :     if (isInline())
      72           0 :         return 0;
      73             : 
      74             :     // JSAtom, JSUndependedString: measure the space for the chars.  For
      75             :     // JSUndependedString, there is no need to count the base string, for the
      76             :     // same reason as JSDependentString above.
      77           0 :     JSFlatString& flat = asFlat();
      78           0 :     return flat.hasLatin1Chars()
      79           0 :            ? mallocSizeOf(flat.rawLatin1Chars())
      80           0 :            : mallocSizeOf(flat.rawTwoByteChars());
      81             : }
      82             : 
      83             : JS::ubi::Node::Size
      84           0 : JS::ubi::Concrete<JSString>::size(mozilla::MallocSizeOf mallocSizeOf) const
      85             : {
      86           0 :     JSString& str = get();
      87             :     size_t size;
      88           0 :     if (str.isAtom())
      89           0 :         size = str.isFatInline() ? sizeof(js::FatInlineAtom) : sizeof(js::NormalAtom);
      90             :     else
      91           0 :         size = str.isFatInline() ? sizeof(JSFatInlineString) : sizeof(JSString);
      92             : 
      93             :     // We can't use mallocSizeof on things in the nursery. At the moment,
      94             :     // strings are never in the nursery, but that may change.
      95           0 :     MOZ_ASSERT(!IsInsideNursery(&str));
      96           0 :     size += str.sizeOfExcludingThis(mallocSizeOf);
      97             : 
      98           0 :     return size;
      99             : }
     100             : 
     101             : const char16_t JS::ubi::Concrete<JSString>::concreteTypeName[] = u"JSString";
     102             : 
     103             : #ifdef DEBUG
     104             : 
     105             : template <typename CharT>
     106             : /*static */ void
     107           0 : JSString::dumpChars(const CharT* s, size_t n, FILE* fp)
     108             : {
     109           0 :     if (n == SIZE_MAX) {
     110           0 :         n = 0;
     111           0 :         while (s[n])
     112           0 :             n++;
     113             :     }
     114             : 
     115           0 :     fputc('"', fp);
     116           0 :     for (size_t i = 0; i < n; i++) {
     117           0 :         char16_t c = s[i];
     118           0 :         if (c == '\n')
     119           0 :             fprintf(fp, "\\n");
     120           0 :         else if (c == '\t')
     121           0 :             fprintf(fp, "\\t");
     122           0 :         else if (c >= 32 && c < 127)
     123           0 :             fputc(s[i], fp);
     124           0 :         else if (c <= 255)
     125           0 :             fprintf(fp, "\\x%02x", unsigned(c));
     126             :         else
     127           0 :             fprintf(fp, "\\u%04x", unsigned(c));
     128             :     }
     129           0 :     fputc('"', fp);
     130           0 : }
     131             : 
     132             : template void
     133             : JSString::dumpChars(const Latin1Char* s, size_t n, FILE* fp);
     134             : 
     135             : template void
     136             : JSString::dumpChars(const char16_t* s, size_t n, FILE* fp);
     137             : 
     138             : void
     139           0 : JSString::dumpCharsNoNewline(FILE* fp)
     140             : {
     141           0 :     if (JSLinearString* linear = ensureLinear(nullptr)) {
     142           0 :         AutoCheckCannotGC nogc;
     143           0 :         if (hasLatin1Chars())
     144           0 :             dumpChars(linear->latin1Chars(nogc), length(), fp);
     145             :         else
     146           0 :             dumpChars(linear->twoByteChars(nogc), length(), fp);
     147             :     } else {
     148           0 :         fprintf(fp, "(oom in JSString::dumpCharsNoNewline)");
     149             :     }
     150           0 : }
     151             : 
     152             : void
     153           0 : JSString::dump(FILE* fp)
     154             : {
     155           0 :     if (JSLinearString* linear = ensureLinear(nullptr)) {
     156           0 :         AutoCheckCannotGC nogc;
     157           0 :         if (hasLatin1Chars()) {
     158           0 :             const Latin1Char* chars = linear->latin1Chars(nogc);
     159             :             fprintf(fp, "JSString* (%p) = Latin1Char * (%p) = ", (void*) this,
     160           0 :                     (void*) chars);
     161           0 :             dumpChars(chars, length(), fp);
     162             :         } else {
     163           0 :             const char16_t* chars = linear->twoByteChars(nogc);
     164             :             fprintf(fp, "JSString* (%p) = char16_t * (%p) = ", (void*) this,
     165           0 :                     (void*) chars);
     166           0 :             dumpChars(chars, length(), fp);
     167             :         }
     168             :     } else {
     169           0 :         fprintf(fp, "(oom in JSString::dump)");
     170             :     }
     171           0 :     fputc('\n', fp);
     172           0 : }
     173             : 
     174             : void
     175           0 : JSString::dumpCharsNoNewline()
     176             : {
     177           0 :     dumpCharsNoNewline(stderr);
     178           0 : }
     179             : 
     180             : void
     181           0 : JSString::dump()
     182             : {
     183           0 :     dump(stderr);
     184           0 : }
     185             : 
     186             : void
     187           0 : JSString::dumpRepresentation(FILE* fp, int indent) const
     188             : {
     189           0 :     if      (isRope())          asRope()        .dumpRepresentation(fp, indent);
     190           0 :     else if (isDependent())     asDependent()   .dumpRepresentation(fp, indent);
     191           0 :     else if (isExternal())      asExternal()    .dumpRepresentation(fp, indent);
     192           0 :     else if (isExtensible())    asExtensible()  .dumpRepresentation(fp, indent);
     193           0 :     else if (isInline())        asInline()      .dumpRepresentation(fp, indent);
     194           0 :     else if (isFlat())          asFlat()        .dumpRepresentation(fp, indent);
     195             :     else
     196           0 :         MOZ_CRASH("Unexpected JSString representation");
     197           0 : }
     198             : 
     199             : void
     200           0 : JSString::dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const
     201             : {
     202           0 :     uint32_t flags = d.u1.flags;
     203             :     // Print the string's address as an actual C++ expression, to facilitate
     204             :     // copy-and-paste into a debugger.
     205           0 :     fprintf(fp, "((%s*) %p) length: %" PRIuSIZE "  flags: 0x%x", subclass, this, length(), flags);
     206           0 :     if (flags & FLAT_BIT)               fputs(" FLAT", fp);
     207           0 :     if (flags & HAS_BASE_BIT)           fputs(" HAS_BASE", fp);
     208           0 :     if (flags & INLINE_CHARS_BIT)       fputs(" INLINE_CHARS", fp);
     209           0 :     if (flags & ATOM_BIT)               fputs(" ATOM", fp);
     210           0 :     if (isPermanentAtom())              fputs(" PERMANENT", fp);
     211           0 :     if (flags & LATIN1_CHARS_BIT)       fputs(" LATIN1", fp);
     212           0 :     if (flags & INDEX_VALUE_BIT)        fprintf(fp, " INDEX_VALUE(%u)", getIndexValue());
     213           0 :     fputc('\n', fp);
     214           0 : }
     215             : 
     216             : void
     217           0 : JSLinearString::dumpRepresentationChars(FILE* fp, int indent) const
     218             : {
     219           0 :     if (hasLatin1Chars()) {
     220           0 :         fprintf(fp, "%*schars: ((Latin1Char*) %p) ", indent, "", rawLatin1Chars());
     221           0 :         dumpChars(rawLatin1Chars(), length());
     222             :     } else {
     223           0 :         fprintf(fp, "%*schars: ((char16_t*) %p) ", indent, "", rawTwoByteChars());
     224           0 :         dumpChars(rawTwoByteChars(), length());
     225             :     }
     226           0 :     fputc('\n', fp);
     227           0 : }
     228             : 
     229             : bool
     230           0 : JSString::equals(const char* s)
     231             : {
     232           0 :     JSLinearString* linear = ensureLinear(nullptr);
     233           0 :     if (!linear) {
     234           0 :         fprintf(stderr, "OOM in JSString::equals!\n");
     235           0 :         return false;
     236             :     }
     237             : 
     238           0 :     return StringEqualsAscii(linear, s);
     239             : }
     240             : #endif /* DEBUG */
     241             : 
     242             : template <typename CharT>
     243             : static MOZ_ALWAYS_INLINE bool
     244         882 : AllocChars(JSString* str, size_t length, CharT** chars, size_t* capacity)
     245             : {
     246             :     /*
     247             :      * String length doesn't include the null char, so include it here before
     248             :      * doubling. Adding the null char after doubling would interact poorly with
     249             :      * round-up malloc schemes.
     250             :      */
     251         882 :     size_t numChars = length + 1;
     252             : 
     253             :     /*
     254             :      * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
     255             :      * next power of 2. This is similar to what we do with arrays; see
     256             :      * JSObject::ensureDenseArrayElements.
     257             :      */
     258             :     static const size_t DOUBLING_MAX = 1024 * 1024;
     259         882 :     numChars = numChars > DOUBLING_MAX ? numChars + (numChars / 8) : RoundUpPow2(numChars);
     260             : 
     261             :     /* Like length, capacity does not include the null char, so take it out. */
     262         882 :     *capacity = numChars - 1;
     263             : 
     264             :     JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(CharT) < UINT32_MAX);
     265         882 :     *chars = str->zone()->pod_malloc<CharT>(numChars);
     266         882 :     return *chars != nullptr;
     267             : }
     268             : 
     269             : bool
     270           4 : JSRope::copyLatin1CharsZ(JSContext* cx, ScopedJSFreePtr<Latin1Char>& out) const
     271             : {
     272           4 :     return copyCharsInternal<Latin1Char>(cx, out, true);
     273             : }
     274             : 
     275             : bool
     276           0 : JSRope::copyTwoByteCharsZ(JSContext* cx, ScopedJSFreePtr<char16_t>& out) const
     277             : {
     278           0 :     return copyCharsInternal<char16_t>(cx, out, true);
     279             : }
     280             : 
     281             : bool
     282           0 : JSRope::copyLatin1Chars(JSContext* cx, ScopedJSFreePtr<Latin1Char>& out) const
     283             : {
     284           0 :     return copyCharsInternal<Latin1Char>(cx, out, false);
     285             : }
     286             : 
     287             : bool
     288           0 : JSRope::copyTwoByteChars(JSContext* cx, ScopedJSFreePtr<char16_t>& out) const
     289             : {
     290           0 :     return copyCharsInternal<char16_t>(cx, out, false);
     291             : }
     292             : 
     293             : template <typename CharT>
     294             : bool
     295           4 : JSRope::copyCharsInternal(JSContext* cx, ScopedJSFreePtr<CharT>& out,
     296             :                           bool nullTerminate) const
     297             : {
     298             :     /*
     299             :      * Perform non-destructive post-order traversal of the rope, splatting
     300             :      * each node's characters into a contiguous buffer.
     301             :      */
     302             : 
     303           4 :     size_t n = length();
     304           4 :     if (cx)
     305           4 :         out.reset(cx->pod_malloc<CharT>(n + 1));
     306             :     else
     307           0 :         out.reset(js_pod_malloc<CharT>(n + 1));
     308             : 
     309           4 :     if (!out)
     310           0 :         return false;
     311             : 
     312           8 :     Vector<const JSString*, 8, SystemAllocPolicy> nodeStack;
     313           4 :     const JSString* str = this;
     314           4 :     CharT* pos = out;
     315             :     while (true) {
     316          20 :         if (str->isRope()) {
     317           4 :             if (!nodeStack.append(str->asRope().rightChild()))
     318           0 :                 return false;
     319           4 :             str = str->asRope().leftChild();
     320             :         } else {
     321           8 :             CopyChars(pos, str->asLinear());
     322           8 :             pos += str->length();
     323           8 :             if (nodeStack.empty())
     324           4 :                 break;
     325           4 :             str = nodeStack.popCopy();
     326             :         }
     327             :     }
     328             : 
     329           4 :     MOZ_ASSERT(pos == out + n);
     330             : 
     331           4 :     if (nullTerminate)
     332           4 :         out[n] = 0;
     333             : 
     334           4 :     return true;
     335             : }
     336             : 
     337             : #ifdef DEBUG
     338             : void
     339           0 : JSRope::dumpRepresentation(FILE* fp, int indent) const
     340             : {
     341           0 :     dumpRepresentationHeader(fp, indent, "JSRope");
     342           0 :     indent += 2;
     343             : 
     344           0 :     fprintf(fp, "%*sleft:  ", indent, "");
     345           0 :     leftChild()->dumpRepresentation(fp, indent);
     346             : 
     347           0 :     fprintf(fp, "%*sright: ", indent, "");
     348           0 :     rightChild()->dumpRepresentation(fp, indent);
     349           0 : }
     350             : #endif
     351             : 
     352             : namespace js {
     353             : 
     354             : template <>
     355             : void
     356         130 : CopyChars(char16_t* dest, const JSLinearString& str)
     357             : {
     358         260 :     AutoCheckCannotGC nogc;
     359         130 :     if (str.hasTwoByteChars())
     360          17 :         PodCopy(dest, str.twoByteChars(nogc), str.length());
     361             :     else
     362         113 :         CopyAndInflateChars(dest, str.latin1Chars(nogc), str.length());
     363         130 : }
     364             : 
     365             : template <>
     366             : void
     367        3838 : CopyChars(Latin1Char* dest, const JSLinearString& str)
     368             : {
     369        7676 :     AutoCheckCannotGC nogc;
     370        3838 :     if (str.hasLatin1Chars()) {
     371        3838 :         PodCopy(dest, str.latin1Chars(nogc), str.length());
     372             :     } else {
     373             :         /*
     374             :          * When we flatten a TwoByte rope, we turn child ropes (including Latin1
     375             :          * ropes) into TwoByte dependent strings. If one of these strings is
     376             :          * also part of another Latin1 rope tree, we can have a Latin1 rope with
     377             :          * a TwoByte descendent and we end up here when we flatten it. Although
     378             :          * the chars are stored as TwoByte, we know they must be in the Latin1
     379             :          * range, so we can safely deflate here.
     380             :          */
     381           0 :         size_t len = str.length();
     382           0 :         const char16_t* chars = str.twoByteChars(nogc);
     383           0 :         for (size_t i = 0; i < len; i++) {
     384           0 :             MOZ_ASSERT(chars[i] <= JSString::MAX_LATIN1_CHAR);
     385           0 :             dest[i] = chars[i];
     386             :         }
     387             :     }
     388        3838 : }
     389             : 
     390             : } /* namespace js */
     391             : 
     392             : template<JSRope::UsingBarrier b, typename CharT>
     393             : JSFlatString*
     394         917 : JSRope::flattenInternal(JSContext* maybecx)
     395             : {
     396             :     /*
     397             :      * Consider the DAG of JSRopes rooted at this JSRope, with non-JSRopes as
     398             :      * its leaves. Mutate the root JSRope into a JSExtensibleString containing
     399             :      * the full flattened text that the root represents, and mutate all other
     400             :      * JSRopes in the interior of the DAG into JSDependentStrings that refer to
     401             :      * this new JSExtensibleString.
     402             :      *
     403             :      * If the leftmost leaf of our DAG is a JSExtensibleString, consider
     404             :      * stealing its buffer for use in our new root, and transforming it into a
     405             :      * JSDependentString too. Do not mutate any of the other leaves.
     406             :      *
     407             :      * Perform a depth-first dag traversal, splatting each node's characters
     408             :      * into a contiguous buffer. Visit each rope node three times:
     409             :      *   1. record position in the buffer and recurse into left child;
     410             :      *   2. recurse into the right child;
     411             :      *   3. transform the node into a dependent string.
     412             :      * To avoid maintaining a stack, tree nodes are mutated to indicate how many
     413             :      * times they have been visited. Since ropes can be dags, a node may be
     414             :      * encountered multiple times during traversal. However, step 3 above leaves
     415             :      * a valid dependent string, so everything works out.
     416             :      *
     417             :      * While ropes avoid all sorts of quadratic cases with string concatenation,
     418             :      * they can't help when ropes are immediately flattened. One idiomatic case
     419             :      * that we'd like to keep linear (and has traditionally been linear in SM
     420             :      * and other JS engines) is:
     421             :      *
     422             :      *   while (...) {
     423             :      *     s += ...
     424             :      *     s.flatten
     425             :      *   }
     426             :      *
     427             :      * Two behaviors accomplish this:
     428             :      *
     429             :      * - When the leftmost non-rope in the DAG we're flattening is a
     430             :      *   JSExtensibleString with sufficient capacity to hold the entire
     431             :      *   flattened string, we just flatten the DAG into its buffer. Then, when
     432             :      *   we transform the root of the DAG from a JSRope into a
     433             :      *   JSExtensibleString, we steal that buffer, and change the victim from a
     434             :      *   JSExtensibleString to a JSDependentString. In this case, the left-hand
     435             :      *   side of the string never needs to be copied.
     436             :      *
     437             :      * - Otherwise, we round up the total flattened size and create a fresh
     438             :      *   JSExtensibleString with that much capacity. If this in turn becomes the
     439             :      *   leftmost leaf of a subsequent flatten, we will hopefully be able to
     440             :      *   fill it, as in the case above.
     441             :      *
     442             :      * Note that, even though the code for creating JSDependentStrings avoids
     443             :      * creating dependents of dependents, we can create that situation here: the
     444             :      * JSExtensibleStrings we transform into JSDependentStrings might have
     445             :      * JSDependentStrings pointing to them already. Stealing the buffer doesn't
     446             :      * change its address, only its owning JSExtensibleString, so all chars()
     447             :      * pointers in the JSDependentStrings are still valid.
     448             :      */
     449         917 :     const size_t wholeLength = length();
     450             :     size_t wholeCapacity;
     451             :     CharT* wholeChars;
     452         917 :     JSString* str = this;
     453             :     CharT* pos;
     454             : 
     455             :     /*
     456             :      * JSString::flattenData is a tagged pointer to the parent node.
     457             :      * The tag indicates what to do when we return to the parent.
     458             :      */
     459             :     static const uintptr_t Tag_Mask = 0x3;
     460             :     static const uintptr_t Tag_FinishNode = 0x0;
     461             :     static const uintptr_t Tag_VisitRightChild = 0x1;
     462             : 
     463        1834 :     AutoCheckCannotGC nogc;
     464             : 
     465             :     /* Find the left most string, containing the first string. */
     466         917 :     JSRope* leftMostRope = this;
     467        5031 :     while (leftMostRope->leftChild()->isRope())
     468        2057 :         leftMostRope = &leftMostRope->leftChild()->asRope();
     469             : 
     470         917 :     if (leftMostRope->leftChild()->isExtensible()) {
     471          40 :         JSExtensibleString& left = leftMostRope->leftChild()->asExtensible();
     472          40 :         size_t capacity = left.capacity();
     473          40 :         if (capacity >= wholeLength && left.hasTwoByteChars() == IsSame<CharT, char16_t>::value) {
     474             :             /*
     475             :              * Simulate a left-most traversal from the root to leftMost->leftChild()
     476             :              * via first_visit_node
     477             :              */
     478          35 :             MOZ_ASSERT(str->isRope());
     479          35 :             while (str != leftMostRope) {
     480             :                 if (b == WithIncrementalBarrier) {
     481           0 :                     JSString::writeBarrierPre(str->d.s.u2.left);
     482           0 :                     JSString::writeBarrierPre(str->d.s.u3.right);
     483             :                 }
     484           0 :                 JSString* child = str->d.s.u2.left;
     485           0 :                 MOZ_ASSERT(child->isRope());
     486           0 :                 str->setNonInlineChars(left.nonInlineChars<CharT>(nogc));
     487           0 :                 child->d.u1.flattenData = uintptr_t(str) | Tag_VisitRightChild;
     488           0 :                 str = child;
     489             :             }
     490             :             if (b == WithIncrementalBarrier) {
     491           0 :                 JSString::writeBarrierPre(str->d.s.u2.left);
     492           0 :                 JSString::writeBarrierPre(str->d.s.u3.right);
     493             :             }
     494          35 :             str->setNonInlineChars(left.nonInlineChars<CharT>(nogc));
     495          35 :             wholeCapacity = capacity;
     496          35 :             wholeChars = const_cast<CharT*>(left.nonInlineChars<CharT>(nogc));
     497          35 :             pos = wholeChars + left.d.u1.length;
     498             :             JS_STATIC_ASSERT(!(EXTENSIBLE_FLAGS & DEPENDENT_FLAGS));
     499          35 :             left.d.u1.flags ^= (EXTENSIBLE_FLAGS | DEPENDENT_FLAGS);
     500          35 :             left.d.s.u3.base = (JSLinearString*)this;  /* will be true on exit */
     501          35 :             StringWriteBarrierPostRemove(maybecx, &left.d.s.u2.left);
     502          35 :             StringWriteBarrierPost(maybecx, (JSString**)&left.d.s.u3.base);
     503          35 :             goto visit_right_child;
     504             :         }
     505             :     }
     506             : 
     507         882 :     if (!AllocChars(this, wholeLength, &wholeChars, &wholeCapacity)) {
     508           0 :         if (maybecx)
     509           0 :             ReportOutOfMemory(maybecx);
     510           0 :         return nullptr;
     511             :     }
     512             : 
     513         882 :     pos = wholeChars;
     514             :     first_visit_node: {
     515             :         if (b == WithIncrementalBarrier) {
     516           4 :             JSString::writeBarrierPre(str->d.s.u2.left);
     517           4 :             JSString::writeBarrierPre(str->d.s.u3.right);
     518             :         }
     519             : 
     520        2973 :         JSString& left = *str->d.s.u2.left;
     521        2973 :         str->setNonInlineChars(pos);
     522        2973 :         StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
     523        2973 :         if (left.isRope()) {
     524             :             /* Return to this node when 'left' done, then goto visit_right_child. */
     525        2070 :             left.d.u1.flattenData = uintptr_t(str) | Tag_VisitRightChild;
     526        2070 :             str = &left;
     527        2070 :             goto first_visit_node;
     528             :         }
     529         903 :         CopyChars(pos, left.asLinear());
     530         903 :         pos += left.length();
     531             :     }
     532             :     visit_right_child: {
     533        3008 :         JSString& right = *str->d.s.u3.right;
     534        3008 :         if (right.isRope()) {
     535             :             /* Return to this node when 'right' done, then goto finish_node. */
     536          21 :             right.d.u1.flattenData = uintptr_t(str) | Tag_FinishNode;
     537          21 :             str = &right;
     538          21 :             goto first_visit_node;
     539             :         }
     540        2987 :         CopyChars(pos, right.asLinear());
     541        2987 :         pos += right.length();
     542             :     }
     543             :     finish_node: {
     544        3008 :         if (str == this) {
     545         917 :             MOZ_ASSERT(pos == wholeChars + wholeLength);
     546         917 :             *pos = '\0';
     547         917 :             str->d.u1.length = wholeLength;
     548             :             if (IsSame<CharT, char16_t>::value)
     549          12 :                 str->d.u1.flags = EXTENSIBLE_FLAGS;
     550             :             else
     551         905 :                 str->d.u1.flags = EXTENSIBLE_FLAGS | LATIN1_CHARS_BIT;
     552         917 :             str->setNonInlineChars(wholeChars);
     553         917 :             str->d.s.u3.capacity = wholeCapacity;
     554         917 :             StringWriteBarrierPostRemove(maybecx, &str->d.s.u2.left);
     555         917 :             StringWriteBarrierPostRemove(maybecx, &str->d.s.u3.right);
     556         917 :             return &this->asFlat();
     557             :         }
     558        2091 :         uintptr_t flattenData = str->d.u1.flattenData;
     559             :         if (IsSame<CharT, char16_t>::value)
     560          36 :             str->d.u1.flags = DEPENDENT_FLAGS;
     561             :         else
     562        2055 :             str->d.u1.flags = DEPENDENT_FLAGS | LATIN1_CHARS_BIT;
     563        2091 :         str->d.u1.length = pos - str->asLinear().nonInlineChars<CharT>(nogc);
     564        2091 :         str->d.s.u3.base = (JSLinearString*)this;       /* will be true on exit */
     565        2091 :         StringWriteBarrierPost(maybecx, (JSString**)&str->d.s.u3.base);
     566        2091 :         str = (JSString*)(flattenData & ~Tag_Mask);
     567        2091 :         if ((flattenData & Tag_Mask) == Tag_VisitRightChild)
     568        2070 :             goto visit_right_child;
     569          21 :         MOZ_ASSERT((flattenData & Tag_Mask) == Tag_FinishNode);
     570          21 :         goto finish_node;
     571             :     }
     572             : }
     573             : 
     574             : template<JSRope::UsingBarrier b>
     575             : JSFlatString*
     576         917 : JSRope::flattenInternal(JSContext* maybecx)
     577             : {
     578         917 :     if (hasTwoByteChars())
     579          12 :         return flattenInternal<b, char16_t>(maybecx);
     580         905 :     return flattenInternal<b, Latin1Char>(maybecx);
     581             : }
     582             : 
     583             : JSFlatString*
     584         917 : JSRope::flatten(JSContext* maybecx)
     585             : {
     586        1834 :     mozilla::Maybe<AutoGeckoProfilerEntry> entry;
     587         917 :     if (maybecx && !maybecx->helperThread())
     588         912 :         entry.emplace(maybecx->runtime(), "JSRope::flatten");
     589             : 
     590         917 :     if (zone()->needsIncrementalBarrier())
     591           4 :         return flattenInternal<WithIncrementalBarrier>(maybecx);
     592         913 :     return flattenInternal<NoBarrier>(maybecx);
     593             : }
     594             : 
     595             : template <AllowGC allowGC>
     596             : static JSLinearString*
     597        7014 : EnsureLinear(JSContext* cx, typename MaybeRooted<JSString*, allowGC>::HandleType string)
     598             : {
     599        7014 :     JSLinearString* linear = string->ensureLinear(cx);
     600             :     // Don't report an exception if GC is not allowed, just return nullptr.
     601         438 :     if (!linear && !allowGC)
     602           0 :         cx->recoverFromOutOfMemory();
     603        7014 :     return linear;
     604             : }
     605             : 
     606             : template <AllowGC allowGC>
     607             : JSString*
     608        7570 : js::ConcatStrings(JSContext* cx,
     609             :                   typename MaybeRooted<JSString*, allowGC>::HandleType left,
     610             :                   typename MaybeRooted<JSString*, allowGC>::HandleType right)
     611             : {
     612        7570 :     MOZ_ASSERT_IF(!left->isAtom(), cx->isInsideCurrentZone(left));
     613        7570 :     MOZ_ASSERT_IF(!right->isAtom(), cx->isInsideCurrentZone(right));
     614             : 
     615        7570 :     size_t leftLen = left->length();
     616        7570 :     if (leftLen == 0)
     617         325 :         return right;
     618             : 
     619        7245 :     size_t rightLen = right->length();
     620        7245 :     if (rightLen == 0)
     621          19 :         return left;
     622             : 
     623        7226 :     size_t wholeLength = leftLen + rightLen;
     624        7226 :     if (MOZ_UNLIKELY(wholeLength > JSString::MAX_LENGTH)) {
     625             :         // Don't report an exception if GC is not allowed, just return nullptr.
     626             :         if (allowGC)
     627           0 :             js::ReportAllocationOverflow(cx);
     628           0 :         return nullptr;
     629             :     }
     630             : 
     631        7226 :     bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars();
     632             :     bool canUseInline = isLatin1
     633             :                         ? JSInlineString::lengthFits<Latin1Char>(wholeLength)
     634        7226 :                         : JSInlineString::lengthFits<char16_t>(wholeLength);
     635        7226 :     if (canUseInline && !cx->helperThread()) {
     636        3507 :         Latin1Char* latin1Buf = nullptr;  // initialize to silence GCC warning
     637        3507 :         char16_t* twoByteBuf = nullptr;  // initialize to silence GCC warning
     638             :         JSInlineString* str = isLatin1
     639        3507 :             ? AllocateInlineString<allowGC>(cx, wholeLength, &latin1Buf)
     640        3507 :             : AllocateInlineString<allowGC>(cx, wholeLength, &twoByteBuf);
     641        3507 :         if (!str)
     642           0 :             return nullptr;
     643             : 
     644        7014 :         AutoCheckCannotGC nogc;
     645        3507 :         JSLinearString* leftLinear = EnsureLinear<allowGC>(cx, left);
     646        3507 :         if (!leftLinear)
     647           0 :             return nullptr;
     648        3507 :         JSLinearString* rightLinear = EnsureLinear<allowGC>(cx, right);
     649        3507 :         if (!rightLinear)
     650           0 :             return nullptr;
     651             : 
     652        3507 :         if (isLatin1) {
     653        3505 :             PodCopy(latin1Buf, leftLinear->latin1Chars(nogc), leftLen);
     654        3505 :             PodCopy(latin1Buf + leftLen, rightLinear->latin1Chars(nogc), rightLen);
     655        3505 :             latin1Buf[wholeLength] = 0;
     656             :         } else {
     657           2 :             if (leftLinear->hasTwoByteChars())
     658           0 :                 PodCopy(twoByteBuf, leftLinear->twoByteChars(nogc), leftLen);
     659             :             else
     660           2 :                 CopyAndInflateChars(twoByteBuf, leftLinear->latin1Chars(nogc), leftLen);
     661           2 :             if (rightLinear->hasTwoByteChars())
     662           2 :                 PodCopy(twoByteBuf + leftLen, rightLinear->twoByteChars(nogc), rightLen);
     663             :             else
     664           0 :                 CopyAndInflateChars(twoByteBuf + leftLen, rightLinear->latin1Chars(nogc), rightLen);
     665           2 :             twoByteBuf[wholeLength] = 0;
     666             :         }
     667             : 
     668        3507 :         return str;
     669             :     }
     670             : 
     671        3719 :     return JSRope::new_<allowGC>(cx, left, right, wholeLength);
     672             : }
     673             : 
     674             : template JSString*
     675             : js::ConcatStrings<CanGC>(JSContext* cx, HandleString left, HandleString right);
     676             : 
     677             : template JSString*
     678             : js::ConcatStrings<NoGC>(JSContext* cx, JSString* const& left, JSString* const& right);
     679             : 
     680             : template <typename CharT>
     681             : JSFlatString*
     682           5 : JSDependentString::undependInternal(JSContext* cx)
     683             : {
     684           5 :     size_t n = length();
     685           5 :     CharT* s = cx->pod_malloc<CharT>(n + 1);
     686           5 :     if (!s)
     687           0 :         return nullptr;
     688             : 
     689          10 :     AutoCheckCannotGC nogc;
     690           5 :     PodCopy(s, nonInlineChars<CharT>(nogc), n);
     691           5 :     s[n] = '\0';
     692           5 :     setNonInlineChars<CharT>(s);
     693             : 
     694             :     /*
     695             :      * Transform *this into an undepended string so 'base' will remain rooted
     696             :      * for the benefit of any other dependent string that depends on *this.
     697             :      */
     698             :     if (IsSame<CharT, Latin1Char>::value)
     699           5 :         d.u1.flags = UNDEPENDED_FLAGS | LATIN1_CHARS_BIT;
     700             :     else
     701           0 :         d.u1.flags = UNDEPENDED_FLAGS;
     702             : 
     703           5 :     return &this->asFlat();
     704             : }
     705             : 
     706             : JSFlatString*
     707           5 : JSDependentString::undepend(JSContext* cx)
     708             : {
     709           5 :     MOZ_ASSERT(JSString::isDependent());
     710           5 :     return hasLatin1Chars()
     711           5 :            ? undependInternal<Latin1Char>(cx)
     712           5 :            : undependInternal<char16_t>(cx);
     713             : }
     714             : 
     715             : #ifdef DEBUG
     716             : void
     717           0 : JSDependentString::dumpRepresentation(FILE* fp, int indent) const
     718             : {
     719           0 :     dumpRepresentationHeader(fp, indent, "JSDependentString");
     720           0 :     indent += 2;
     721             : 
     722           0 :     if (mozilla::Maybe<size_t> offset = baseOffset())
     723           0 :         fprintf(fp, "%*soffset: %" PRIuSIZE "\n", indent, "", *offset);
     724             : 
     725           0 :     fprintf(fp, "%*sbase: ", indent, "");
     726           0 :     base()->dumpRepresentation(fp, indent);
     727           0 : }
     728             : #endif
     729             : 
     730             : template <typename CharT>
     731             : /* static */ bool
     732        1199 : JSFlatString::isIndexSlow(const CharT* s, size_t length, uint32_t* indexp)
     733             : {
     734        1199 :     CharT ch = *s;
     735             : 
     736        1199 :     if (!JS7_ISDEC(ch))
     737           0 :         return false;
     738             : 
     739        1199 :     if (length > UINT32_CHAR_BUFFER_LENGTH)
     740           0 :         return false;
     741             : 
     742             :     /*
     743             :      * Make sure to account for the '\0' at the end of characters, dereferenced
     744             :      * in the loop below.
     745             :      */
     746        1199 :     RangedPtr<const CharT> cp(s, length + 1);
     747        1199 :     const RangedPtr<const CharT> end(s + length, s, length + 1);
     748             : 
     749        1199 :     uint32_t index = JS7_UNDEC(*cp++);
     750        1199 :     uint32_t oldIndex = 0;
     751        1199 :     uint32_t c = 0;
     752             : 
     753        1199 :     if (index != 0) {
     754        5858 :         while (JS7_ISDEC(*cp)) {
     755        2331 :             oldIndex = index;
     756        2331 :             c = JS7_UNDEC(*cp);
     757        2331 :             index = 10 * index + c;
     758        2331 :             cp++;
     759             :         }
     760             :     }
     761             : 
     762             :     /* It's not an element if there are characters after the number. */
     763        1199 :     if (cp != end)
     764           0 :         return false;
     765             : 
     766             :     /*
     767             :      * Look out for "4294967296" and larger-number strings that fit in
     768             :      * UINT32_CHAR_BUFFER_LENGTH: only unsigned 32-bit integers shall pass.
     769             :      */
     770        1199 :     if (oldIndex < UINT32_MAX / 10 || (oldIndex == UINT32_MAX / 10 && c <= (UINT32_MAX % 10))) {
     771        1199 :         *indexp = index;
     772        1199 :         return true;
     773             :     }
     774             : 
     775           0 :     return false;
     776             : }
     777             : 
     778             : template bool
     779             : JSFlatString::isIndexSlow(const Latin1Char* s, size_t length, uint32_t* indexp);
     780             : 
     781             : template bool
     782             : JSFlatString::isIndexSlow(const char16_t* s, size_t length, uint32_t* indexp);
     783             : 
     784             : /*
     785             :  * Set up some tools to make it easier to generate large tables. After constant
     786             :  * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
     787             :  * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
     788             :  * To use this, define R appropriately, then use Rn(0) (for some value of n), then
     789             :  * undefine R.
     790             :  */
     791             : #define R2(n) R(n),  R((n) + (1 << 0)),  R((n) + (2 << 0)),  R((n) + (3 << 0))
     792             : #define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2))
     793             : #define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4))
     794             : #define R7(n) R6(n), R6((n) + (1 << 6))
     795             : 
     796             : /*
     797             :  * This is used when we generate our table of short strings, so the compiler is
     798             :  * happier if we use |c| as few times as possible.
     799             :  */
     800             : #define FROM_SMALL_CHAR(c) Latin1Char((c) + ((c) < 10 ? '0' :      \
     801             :                                              (c) < 36 ? 'a' - 10 : \
     802             :                                              'A' - 36))
     803             : 
     804             : /*
     805             :  * Declare length-2 strings. We only store strings where both characters are
     806             :  * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
     807             :  * the lowercase letters, and the next 26 are the uppercase letters.
     808             :  */
     809             : #define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' :              \
     810             :                           (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 :         \
     811             :                           (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 :         \
     812             :                           StaticStrings::INVALID_SMALL_CHAR)
     813             : 
     814             : #define R TO_SMALL_CHAR
     815             : const StaticStrings::SmallChar StaticStrings::toSmallChar[] = { R7(0) };
     816             : #undef R
     817             : 
     818             : #undef R2
     819             : #undef R4
     820             : #undef R6
     821             : #undef R7
     822             : 
     823             : bool
     824           3 : StaticStrings::init(JSContext* cx)
     825             : {
     826           6 :     AutoLockForExclusiveAccess lock(cx);
     827           6 :     AutoAtomsCompartment ac(cx, lock);
     828             : 
     829             :     static_assert(UNIT_STATIC_LIMIT - 1 <= JSString::MAX_LATIN1_CHAR,
     830             :                   "Unit strings must fit in Latin1Char.");
     831             : 
     832             :     using Latin1Range = mozilla::Range<const Latin1Char>;
     833             : 
     834         771 :     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
     835         768 :         Latin1Char buffer[] = { Latin1Char(i), '\0' };
     836         768 :         JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 1));
     837         768 :         if (!s)
     838           0 :             return false;
     839         768 :         HashNumber hash = mozilla::HashString(buffer, 1);
     840         768 :         unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(hash);
     841             :     }
     842             : 
     843       12291 :     for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
     844       12288 :         Latin1Char buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' };
     845       12288 :         JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 2));
     846       12288 :         if (!s)
     847           0 :             return false;
     848       12288 :         HashNumber hash = mozilla::HashString(buffer, 2);
     849       12288 :         length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(hash);
     850             :     }
     851             : 
     852         771 :     for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
     853         768 :         if (i < 10) {
     854          30 :             intStaticTable[i] = unitStaticTable[i + '0'];
     855         738 :         } else if (i < 100) {
     856         270 :             size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) +
     857         270 :                 TO_SMALL_CHAR((i % 10) + '0');
     858         270 :             intStaticTable[i] = length2StaticTable[index];
     859             :         } else {
     860         468 :             Latin1Char buffer[] = { Latin1Char('0' + (i / 100)),
     861         468 :                                     Latin1Char('0' + ((i / 10) % 10)),
     862         468 :                                     Latin1Char('0' + (i % 10)),
     863        1872 :                                     '\0' };
     864         468 :             JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 3));
     865         468 :             if (!s)
     866           0 :                 return false;
     867         468 :             HashNumber hash = mozilla::HashString(buffer, 3);
     868         468 :             intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(hash);
     869             :         }
     870             : 
     871             :         // Static string initialization can not race, so allow even without the lock.
     872         768 :         intStaticTable[i]->maybeInitializeIndex(i, true);
     873             :     }
     874             : 
     875           3 :     return true;
     876             : }
     877             : 
     878             : void
     879           1 : StaticStrings::trace(JSTracer* trc)
     880             : {
     881             :     /* These strings never change, so barriers are not needed. */
     882             : 
     883         257 :     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++)
     884         256 :         TraceProcessGlobalRoot(trc, unitStaticTable[i], "unit-static-string");
     885             : 
     886        4097 :     for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++)
     887        4096 :         TraceProcessGlobalRoot(trc, length2StaticTable[i], "length2-static-string");
     888             : 
     889             :     /* This may mark some strings more than once, but so be it. */
     890         257 :     for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++)
     891         256 :         TraceProcessGlobalRoot(trc, intStaticTable[i], "int-static-string");
     892           1 : }
     893             : 
     894             : template <typename CharT>
     895             : /* static */ bool
     896       32110 : StaticStrings::isStatic(const CharT* chars, size_t length)
     897             : {
     898       32110 :     switch (length) {
     899             :       case 1: {
     900           0 :         char16_t c = chars[0];
     901           0 :         return c < UNIT_STATIC_LIMIT;
     902             :       }
     903             :       case 2:
     904          24 :         return fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]);
     905             :       case 3:
     906          94 :         if ('1' <= chars[0] && chars[0] <= '9' &&
     907           0 :             '0' <= chars[1] && chars[1] <= '9' &&
     908           0 :             '0' <= chars[2] && chars[2] <= '9') {
     909           0 :             int i = (chars[0] - '0') * 100 +
     910           0 :                       (chars[1] - '0') * 10 +
     911           0 :                       (chars[2] - '0');
     912             : 
     913           0 :             return unsigned(i) < INT_STATIC_LIMIT;
     914             :         }
     915          94 :         return false;
     916             :       default:
     917       31992 :         return false;
     918             :     }
     919             : }
     920             : 
     921             : /* static */ bool
     922       32110 : StaticStrings::isStatic(JSAtom* atom)
     923             : {
     924       64220 :     AutoCheckCannotGC nogc;
     925       32110 :     return atom->hasLatin1Chars()
     926       32110 :            ? isStatic(atom->latin1Chars(nogc), atom->length())
     927       64220 :            : isStatic(atom->twoByteChars(nogc), atom->length());
     928             : }
     929             : 
     930             : bool
     931         100 : AutoStableStringChars::init(JSContext* cx, JSString* s)
     932             : {
     933         200 :     RootedLinearString linearString(cx, s->ensureLinear(cx));
     934         100 :     if (!linearString)
     935           0 :         return false;
     936             : 
     937         100 :     MOZ_ASSERT(state_ == Uninitialized);
     938             : 
     939         100 :     if (linearString->isExternal() && !linearString->ensureFlat(cx))
     940           0 :         return false;
     941             : 
     942             :     // If the chars are inline then we need to copy them since they may be moved
     943             :     // by a compacting GC.
     944         100 :     if (baseIsInline(linearString)) {
     945         224 :         return linearString->hasTwoByteChars() ? copyTwoByteChars(cx, linearString)
     946         224 :                                                : copyLatin1Chars(cx, linearString);
     947             :     }
     948             : 
     949          44 :     if (linearString->hasLatin1Chars()) {
     950          40 :         state_ = Latin1;
     951          40 :         latin1Chars_ = linearString->rawLatin1Chars();
     952             :     } else {
     953           4 :         state_ = TwoByte;
     954           4 :         twoByteChars_ = linearString->rawTwoByteChars();
     955             :     }
     956             : 
     957          44 :     s_ = linearString;
     958          44 :     return true;
     959             : }
     960             : 
     961             : bool
     962           6 : AutoStableStringChars::initTwoByte(JSContext* cx, JSString* s)
     963             : {
     964          12 :     RootedLinearString linearString(cx, s->ensureLinear(cx));
     965           6 :     if (!linearString)
     966           0 :         return false;
     967             : 
     968           6 :     MOZ_ASSERT(state_ == Uninitialized);
     969             : 
     970           6 :     if (linearString->hasLatin1Chars())
     971           0 :         return copyAndInflateLatin1Chars(cx, linearString);
     972             : 
     973           6 :     if (linearString->isExternal() && !linearString->ensureFlat(cx))
     974           0 :         return false;
     975             : 
     976             :     // If the chars are inline then we need to copy them since they may be moved
     977             :     // by a compacting GC.
     978           6 :     if (baseIsInline(linearString))
     979           0 :         return copyTwoByteChars(cx, linearString);
     980             : 
     981           6 :     state_ = TwoByte;
     982           6 :     twoByteChars_ = linearString->rawTwoByteChars();
     983           6 :     s_ = linearString;
     984           6 :     return true;
     985             : }
     986             : 
     987         106 : bool AutoStableStringChars::baseIsInline(HandleLinearString linearString)
     988             : {
     989         106 :     JSString* base = linearString;
     990         110 :     while (base->isDependent())
     991           2 :         base = base->asDependent().base();
     992         106 :     return base->isInline();
     993             : }
     994             : 
     995             : template <typename T>
     996             : T*
     997          56 : AutoStableStringChars::allocOwnChars(JSContext* cx, size_t count)
     998             : {
     999             :     static_assert(
    1000             :         InlineCapacity >= sizeof(JS::Latin1Char) * (JSFatInlineString::MAX_LENGTH_LATIN1 + 1) &&
    1001             :         InlineCapacity >= sizeof(char16_t) * (JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1),
    1002             :         "InlineCapacity too small to hold fat inline strings");
    1003             : 
    1004             :     static_assert((JSString::MAX_LENGTH & mozilla::tl::MulOverflowMask<sizeof(T)>::value) == 0,
    1005             :                   "Size calculation can overflow");
    1006          56 :     MOZ_ASSERT(count <= (JSString::MAX_LENGTH + 1));
    1007          56 :     size_t size = sizeof(T) * count;
    1008             : 
    1009          56 :     ownChars_.emplace(cx);
    1010          56 :     if (!ownChars_->resize(size)) {
    1011           0 :         ownChars_.reset();
    1012           0 :         return nullptr;
    1013             :     }
    1014             : 
    1015          56 :     return reinterpret_cast<T*>(ownChars_->begin());
    1016             : }
    1017             : 
    1018             : bool
    1019           0 : AutoStableStringChars::copyAndInflateLatin1Chars(JSContext* cx, HandleLinearString linearString)
    1020             : {
    1021           0 :     char16_t* chars = allocOwnChars<char16_t>(cx, linearString->length() + 1);
    1022           0 :     if (!chars)
    1023           0 :         return false;
    1024             : 
    1025           0 :     CopyAndInflateChars(chars, linearString->rawLatin1Chars(),
    1026           0 :                         linearString->length());
    1027           0 :     chars[linearString->length()] = 0;
    1028             : 
    1029           0 :     state_ = TwoByte;
    1030           0 :     twoByteChars_ = chars;
    1031           0 :     s_ = linearString;
    1032           0 :     return true;
    1033             : }
    1034             : 
    1035             : bool
    1036          56 : AutoStableStringChars::copyLatin1Chars(JSContext* cx, HandleLinearString linearString)
    1037             : {
    1038          56 :     size_t length = linearString->length();
    1039          56 :     JS::Latin1Char* chars = allocOwnChars<JS::Latin1Char>(cx, length + 1);
    1040          56 :     if (!chars)
    1041           0 :         return false;
    1042             : 
    1043          56 :     PodCopy(chars, linearString->rawLatin1Chars(), length);
    1044          56 :     chars[length] = 0;
    1045             : 
    1046          56 :     state_ = Latin1;
    1047          56 :     latin1Chars_ = chars;
    1048          56 :     s_ = linearString;
    1049          56 :     return true;
    1050             : }
    1051             : 
    1052             : bool
    1053           0 : AutoStableStringChars::copyTwoByteChars(JSContext* cx, HandleLinearString linearString)
    1054             : {
    1055           0 :     size_t length = linearString->length();
    1056           0 :     char16_t* chars = allocOwnChars<char16_t>(cx, length + 1);
    1057           0 :     if (!chars)
    1058           0 :         return false;
    1059             : 
    1060           0 :     PodCopy(chars, linearString->rawTwoByteChars(), length);
    1061           0 :     chars[length] = 0;
    1062             : 
    1063           0 :     state_ = TwoByte;
    1064           0 :     twoByteChars_ = chars;
    1065           0 :     s_ = linearString;
    1066           0 :     return true;
    1067             : }
    1068             : 
    1069             : JSFlatString*
    1070        1130 : JSString::ensureFlat(JSContext* cx)
    1071             : {
    1072        1130 :     if (isFlat())
    1073        1109 :         return &asFlat();
    1074          21 :     if (isDependent())
    1075           5 :         return asDependent().undepend(cx);
    1076          16 :     if (isRope())
    1077           1 :         return asRope().flatten(cx);
    1078          15 :     return asExternal().ensureFlat(cx);
    1079             : }
    1080             : 
    1081             : JSFlatString*
    1082          15 : JSExternalString::ensureFlat(JSContext* cx)
    1083             : {
    1084          15 :     MOZ_ASSERT(hasTwoByteChars());
    1085             : 
    1086          15 :     size_t n = length();
    1087          15 :     char16_t* s = cx->pod_malloc<char16_t>(n + 1);
    1088          15 :     if (!s)
    1089           0 :         return nullptr;
    1090             : 
    1091             :     // Copy the chars before finalizing the string.
    1092             :     {
    1093          30 :         AutoCheckCannotGC nogc;
    1094          15 :         PodCopy(s, nonInlineChars<char16_t>(nogc), n);
    1095          15 :         s[n] = '\0';
    1096             :     }
    1097             : 
    1098             :     // Release the external chars.
    1099          15 :     finalize(cx->runtime()->defaultFreeOp());
    1100             : 
    1101             :     // Transform the string into a non-external, flat string.
    1102          15 :     setNonInlineChars<char16_t>(s);
    1103          15 :     d.u1.flags = FLAT_BIT;
    1104             : 
    1105          15 :     return &this->asFlat();
    1106             : }
    1107             : 
    1108             : #ifdef DEBUG
    1109             : void
    1110           0 : JSAtom::dump(FILE* fp)
    1111             : {
    1112           0 :     fprintf(fp, "JSAtom* (%p) = ", (void*) this);
    1113           0 :     this->JSString::dump(fp);
    1114           0 : }
    1115             : 
    1116             : void
    1117           0 : JSAtom::dump()
    1118             : {
    1119           0 :     dump(stderr);
    1120           0 : }
    1121             : 
    1122             : void
    1123           0 : JSExternalString::dumpRepresentation(FILE* fp, int indent) const
    1124             : {
    1125           0 :     dumpRepresentationHeader(fp, indent, "JSExternalString");
    1126           0 :     indent += 2;
    1127             : 
    1128           0 :     fprintf(fp, "%*sfinalizer: ((JSStringFinalizer*) %p)\n", indent, "", externalFinalizer());
    1129           0 :     dumpRepresentationChars(fp, indent);
    1130           0 : }
    1131             : #endif /* DEBUG */
    1132             : 
    1133             : JSLinearString*
    1134         570 : js::NewDependentString(JSContext* cx, JSString* baseArg, size_t start, size_t length)
    1135             : {
    1136         570 :     if (length == 0)
    1137          78 :         return cx->emptyString();
    1138             : 
    1139         492 :     JSLinearString* base = baseArg->ensureLinear(cx);
    1140         492 :     if (!base)
    1141           0 :         return nullptr;
    1142             : 
    1143         492 :     if (start == 0 && length == base->length())
    1144         129 :         return base;
    1145             : 
    1146         363 :     if (base->hasTwoByteChars()) {
    1147          27 :         AutoCheckCannotGC nogc;
    1148          18 :         const char16_t* chars = base->twoByteChars(nogc) + start;
    1149          18 :         if (JSLinearString* staticStr = cx->staticStrings().lookup(chars, length))
    1150           9 :             return staticStr;
    1151             :     } else {
    1152         670 :         AutoCheckCannotGC nogc;
    1153         345 :         const Latin1Char* chars = base->latin1Chars(nogc) + start;
    1154         345 :         if (JSLinearString* staticStr = cx->staticStrings().lookup(chars, length))
    1155          20 :             return staticStr;
    1156             :     }
    1157             : 
    1158         334 :     return JSDependentString::new_(cx, base, start, length);
    1159             : }
    1160             : 
    1161             : static bool
    1162       21452 : CanStoreCharsAsLatin1(const char16_t* s, size_t length)
    1163             : {
    1164      383634 :     for (const char16_t* end = s + length; s < end; ++s) {
    1165      362205 :         if (*s > JSString::MAX_LATIN1_CHAR)
    1166          23 :             return false;
    1167             :     }
    1168             : 
    1169       21429 :     return true;
    1170             : }
    1171             : 
    1172             : static bool
    1173           0 : CanStoreCharsAsLatin1(const Latin1Char* s, size_t length)
    1174             : {
    1175           0 :     MOZ_CRASH("Shouldn't be called for Latin1 chars");
    1176             : }
    1177             : 
    1178             : template <AllowGC allowGC>
    1179             : static MOZ_ALWAYS_INLINE JSInlineString*
    1180       17064 : NewInlineStringDeflated(JSContext* cx, mozilla::Range<const char16_t> chars)
    1181             : {
    1182       17064 :     size_t len = chars.length();
    1183             :     Latin1Char* storage;
    1184       17064 :     JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage);
    1185       17064 :     if (!str)
    1186           0 :         return nullptr;
    1187             : 
    1188      216142 :     for (size_t i = 0; i < len; i++) {
    1189      199078 :         MOZ_ASSERT(chars[i] <= JSString::MAX_LATIN1_CHAR);
    1190      199078 :         storage[i] = Latin1Char(chars[i]);
    1191             :     }
    1192       17064 :     storage[len] = '\0';
    1193       17064 :     return str;
    1194             : }
    1195             : 
    1196             : template <typename CharT>
    1197             : static MOZ_ALWAYS_INLINE JSFlatString*
    1198       69715 : TryEmptyOrStaticString(JSContext* cx, const CharT* chars, size_t n)
    1199             : {
    1200             :     // Measurements on popular websites indicate empty strings are pretty common
    1201             :     // and most strings with length 1 or 2 are in the StaticStrings table. For
    1202             :     // length 3 strings that's only about 1%, so we check n <= 2.
    1203       69715 :     if (n <= 2) {
    1204         278 :         if (n == 0)
    1205          42 :             return cx->emptyString();
    1206             : 
    1207         236 :         if (JSFlatString* str = cx->staticStrings().lookup(chars, n))
    1208          38 :             return str;
    1209             :     }
    1210             : 
    1211       69635 :     return nullptr;
    1212             : }
    1213             : 
    1214             : template <AllowGC allowGC>
    1215             : static JSFlatString*
    1216       21429 : NewStringDeflated(JSContext* cx, const char16_t* s, size_t n)
    1217             : {
    1218       21429 :     if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n))
    1219          25 :         return str;
    1220             : 
    1221       21404 :     if (JSInlineString::lengthFits<Latin1Char>(n))
    1222       17064 :         return NewInlineStringDeflated<allowGC>(cx, mozilla::Range<const char16_t>(s, n));
    1223             : 
    1224        8680 :     ScopedJSFreePtr<Latin1Char> news(cx->pod_malloc<Latin1Char>(n + 1));
    1225        4340 :     if (!news)
    1226           0 :         return nullptr;
    1227             : 
    1228      167250 :     for (size_t i = 0; i < n; i++) {
    1229      162910 :         MOZ_ASSERT(s[i] <= JSString::MAX_LATIN1_CHAR);
    1230      162910 :         news.get()[i] = Latin1Char(s[i]);
    1231             :     }
    1232        4340 :     news[n] = '\0';
    1233             : 
    1234        4340 :     JSFlatString* str = JSFlatString::new_<allowGC>(cx, news.get(), n);
    1235        4340 :     if (!str)
    1236           0 :         return nullptr;
    1237             : 
    1238        4340 :     news.forget();
    1239        4340 :     return str;
    1240             : }
    1241             : 
    1242             : template <AllowGC allowGC>
    1243             : static JSFlatString*
    1244           0 : NewStringDeflated(JSContext* cx, const Latin1Char* s, size_t n)
    1245             : {
    1246           0 :     MOZ_CRASH("Shouldn't be called for Latin1 chars");
    1247             : }
    1248             : 
    1249             : template <AllowGC allowGC, typename CharT>
    1250             : JSFlatString*
    1251        1129 : js::NewStringDontDeflate(JSContext* cx, CharT* chars, size_t length)
    1252             : {
    1253        1129 :     if (JSFlatString* str = TryEmptyOrStaticString(cx, chars, length)) {
    1254             :         // Free |chars| because we're taking possession of it, but it's no
    1255             :         // longer needed because we use the static string instead.
    1256          11 :         js_free(chars);
    1257          11 :         return str;
    1258             :     }
    1259             : 
    1260        1118 :     if (JSInlineString::lengthFits<CharT>(length)) {
    1261             :         JSInlineString* str =
    1262         497 :             NewInlineString<allowGC>(cx, mozilla::Range<const CharT>(chars, length));
    1263         497 :         if (!str)
    1264           0 :             return nullptr;
    1265             : 
    1266         497 :         js_free(chars);
    1267         497 :         return str;
    1268             :     }
    1269             : 
    1270         621 :     return JSFlatString::new_<allowGC>(cx, chars, length);
    1271             : }
    1272             : 
    1273             : template JSFlatString*
    1274             : js::NewStringDontDeflate<CanGC>(JSContext* cx, char16_t* chars, size_t length);
    1275             : 
    1276             : template JSFlatString*
    1277             : js::NewStringDontDeflate<NoGC>(JSContext* cx, char16_t* chars, size_t length);
    1278             : 
    1279             : template JSFlatString*
    1280             : js::NewStringDontDeflate<CanGC>(JSContext* cx, Latin1Char* chars, size_t length);
    1281             : 
    1282             : template JSFlatString*
    1283             : js::NewStringDontDeflate<NoGC>(JSContext* cx, Latin1Char* chars, size_t length);
    1284             : 
    1285             : template <AllowGC allowGC, typename CharT>
    1286             : JSFlatString*
    1287         777 : js::NewString(JSContext* cx, CharT* chars, size_t length)
    1288             : {
    1289         777 :     if (IsSame<CharT, char16_t>::value && CanStoreCharsAsLatin1(chars, length)) {
    1290         177 :         JSFlatString* s = NewStringDeflated<allowGC>(cx, chars, length);
    1291         177 :         if (!s)
    1292           0 :             return nullptr;
    1293             : 
    1294             :         // Free |chars| because we're taking possession of it but not using it.
    1295         177 :         js_free(chars);
    1296         177 :         return s;
    1297             :     }
    1298             : 
    1299         600 :     return NewStringDontDeflate<allowGC>(cx, chars, length);
    1300             : }
    1301             : 
    1302             : template JSFlatString*
    1303             : js::NewString<CanGC>(JSContext* cx, char16_t* chars, size_t length);
    1304             : 
    1305             : template JSFlatString*
    1306             : js::NewString<NoGC>(JSContext* cx, char16_t* chars, size_t length);
    1307             : 
    1308             : template JSFlatString*
    1309             : js::NewString<CanGC>(JSContext* cx, Latin1Char* chars, size_t length);
    1310             : 
    1311             : template JSFlatString*
    1312             : js::NewString<NoGC>(JSContext* cx, Latin1Char* chars, size_t length);
    1313             : 
    1314             : namespace js {
    1315             : 
    1316             : template <AllowGC allowGC, typename CharT>
    1317             : JSFlatString*
    1318       46689 : NewStringCopyNDontDeflate(JSContext* cx, const CharT* s, size_t n)
    1319             : {
    1320       46689 :     if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n))
    1321          19 :         return str;
    1322             : 
    1323       46670 :     if (JSInlineString::lengthFits<CharT>(n))
    1324       34061 :         return NewInlineString<allowGC>(cx, mozilla::Range<const CharT>(s, n));
    1325             : 
    1326       25218 :     ScopedJSFreePtr<CharT> news(cx->pod_malloc<CharT>(n + 1));
    1327       12609 :     if (!news) {
    1328             :         if (!allowGC)
    1329           0 :             cx->recoverFromOutOfMemory();
    1330           0 :         return nullptr;
    1331             :     }
    1332             : 
    1333       12609 :     PodCopy(news.get(), s, n);
    1334       12609 :     news[n] = 0;
    1335             : 
    1336       12609 :     JSFlatString* str = JSFlatString::new_<allowGC>(cx, news.get(), n);
    1337       12609 :     if (!str)
    1338           0 :         return nullptr;
    1339             : 
    1340       12609 :     news.forget();
    1341       12609 :     return str;
    1342             : }
    1343             : 
    1344             : template JSFlatString*
    1345             : NewStringCopyNDontDeflate<CanGC>(JSContext* cx, const char16_t* s, size_t n);
    1346             : 
    1347             : template JSFlatString*
    1348             : NewStringCopyNDontDeflate<NoGC>(JSContext* cx, const char16_t* s, size_t n);
    1349             : 
    1350             : template JSFlatString*
    1351             : NewStringCopyNDontDeflate<CanGC>(JSContext* cx, const Latin1Char* s, size_t n);
    1352             : 
    1353             : template JSFlatString*
    1354             : NewStringCopyNDontDeflate<NoGC>(JSContext* cx, const Latin1Char* s, size_t n);
    1355             : 
    1356             : JSFlatString*
    1357           0 : NewLatin1StringZ(JSContext* cx, UniqueChars chars)
    1358             : {
    1359           0 :     JSFlatString* str = NewString<CanGC>(cx, (Latin1Char*)chars.get(), strlen(chars.get()));
    1360           0 :     if (!str)
    1361           0 :         return nullptr;
    1362             : 
    1363           0 :     mozilla::Unused << chars.release();
    1364           0 :     return str;
    1365             : }
    1366             : 
    1367             : template <AllowGC allowGC, typename CharT>
    1368             : JSFlatString*
    1369       67859 : NewStringCopyN(JSContext* cx, const CharT* s, size_t n)
    1370             : {
    1371       67859 :     if (IsSame<CharT, char16_t>::value && CanStoreCharsAsLatin1(s, n))
    1372       21252 :         return NewStringDeflated<allowGC>(cx, s, n);
    1373             : 
    1374       46607 :     return NewStringCopyNDontDeflate<allowGC>(cx, s, n);
    1375             : }
    1376             : 
    1377             : template JSFlatString*
    1378             : NewStringCopyN<CanGC>(JSContext* cx, const char16_t* s, size_t n);
    1379             : 
    1380             : template JSFlatString*
    1381             : NewStringCopyN<NoGC>(JSContext* cx, const char16_t* s, size_t n);
    1382             : 
    1383             : template JSFlatString*
    1384             : NewStringCopyN<CanGC>(JSContext* cx, const Latin1Char* s, size_t n);
    1385             : 
    1386             : template JSFlatString*
    1387             : NewStringCopyN<NoGC>(JSContext* cx, const Latin1Char* s, size_t n);
    1388             : 
    1389             : template <js::AllowGC allowGC>
    1390             : JSFlatString*
    1391           2 : NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8)
    1392             : {
    1393           2 :     JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
    1394           2 :     if (encoding == JS::SmallestEncoding::ASCII)
    1395           2 :         return NewStringCopyN<allowGC>(cx, utf8.begin().get(), utf8.length());
    1396             : 
    1397             :     size_t length;
    1398           0 :     if (encoding == JS::SmallestEncoding::Latin1) {
    1399           0 :         Latin1Char* latin1 = UTF8CharsToNewLatin1CharsZ(cx, utf8, &length).get();
    1400           0 :         if (!latin1)
    1401           0 :             return nullptr;
    1402             : 
    1403           0 :         JSFlatString* result = NewString<allowGC>(cx, latin1, length);
    1404           0 :         if (!result)
    1405           0 :             js_free((void*)latin1);
    1406           0 :         return result;
    1407             :     }
    1408             : 
    1409           0 :     MOZ_ASSERT(encoding == JS::SmallestEncoding::UTF16);
    1410             : 
    1411           0 :     char16_t* utf16 = UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get();
    1412           0 :     if (!utf16)
    1413           0 :         return nullptr;
    1414             : 
    1415           0 :     JSFlatString* result = NewString<allowGC>(cx, utf16, length);
    1416           0 :     if (!result)
    1417           0 :         js_free((void*)utf16);
    1418           0 :     return result;
    1419             : }
    1420             : 
    1421             : template JSFlatString*
    1422             : NewStringCopyUTF8N<CanGC>(JSContext* cx, const JS::UTF8Chars utf8);
    1423             : 
    1424             : MOZ_ALWAYS_INLINE JSString*
    1425         448 : ExternalStringCache::lookup(const char16_t* chars, size_t len) const
    1426             : {
    1427         896 :     AutoCheckCannotGC nogc;
    1428             : 
    1429        1936 :     for (size_t i = 0; i < NumEntries; i++) {
    1430        1581 :         JSString* str = entries_[i];
    1431        1581 :         if (!str || str->length() != len)
    1432        1384 :             continue;
    1433             : 
    1434         197 :         const char16_t* strChars = str->asLinear().nonInlineTwoByteChars(nogc);
    1435         197 :         if (chars == strChars) {
    1436             :             // Note that we don't need an incremental barrier here or below.
    1437             :             // The cache is purged on GC so any string we get from the cache
    1438             :             // must have been allocated after the GC started.
    1439          78 :             return str;
    1440             :         }
    1441             : 
    1442             :         // Compare the chars. Don't do this for long strings as it will be
    1443             :         // faster to allocate a new external string.
    1444             :         static const size_t MaxLengthForCharComparison = 100;
    1445         119 :         if (len <= MaxLengthForCharComparison && PodEqual(chars, strChars, len))
    1446          15 :             return str;
    1447             :     }
    1448             : 
    1449         355 :     return nullptr;
    1450             : }
    1451             : 
    1452             : MOZ_ALWAYS_INLINE void
    1453         355 : ExternalStringCache::put(JSString* str)
    1454             : {
    1455         355 :     MOZ_ASSERT(str->isExternal());
    1456             : 
    1457        1420 :     for (size_t i = NumEntries - 1; i > 0; i--)
    1458        1065 :         entries_[i] = entries_[i - 1];
    1459             : 
    1460         355 :     entries_[0] = str;
    1461         355 : }
    1462             : 
    1463             : JSString*
    1464         470 : NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n, const JSStringFinalizer* fin,
    1465             :                        bool* allocatedExternal)
    1466             : {
    1467         470 :     if (JSString* str = TryEmptyOrStaticString(cx, s, n)) {
    1468          22 :         *allocatedExternal = false;
    1469          22 :         return str;
    1470             :     }
    1471             : 
    1472         448 :     ExternalStringCache& cache = cx->zone()->externalStringCache();
    1473         448 :     if (JSString* str = cache.lookup(s, n)) {
    1474          93 :         *allocatedExternal = false;
    1475          93 :         return str;
    1476             :     }
    1477             : 
    1478         355 :     JSString* str = JSExternalString::new_(cx, s, n, fin);
    1479         355 :     if (!str)
    1480           0 :         return nullptr;
    1481             : 
    1482         355 :     *allocatedExternal = true;
    1483         355 :     cache.put(str);
    1484         355 :     return str;
    1485             : }
    1486             : 
    1487             : } /* namespace js */
    1488             : 
    1489             : #ifdef DEBUG
    1490             : void
    1491           0 : JSExtensibleString::dumpRepresentation(FILE* fp, int indent) const
    1492             : {
    1493           0 :     dumpRepresentationHeader(fp, indent, "JSExtensibleString");
    1494           0 :     indent += 2;
    1495             : 
    1496           0 :     fprintf(fp, "%*scapacity: %" PRIuSIZE "\n", indent, "", capacity());
    1497           0 :     dumpRepresentationChars(fp, indent);
    1498           0 : }
    1499             : 
    1500             : void
    1501           0 : JSInlineString::dumpRepresentation(FILE* fp, int indent) const
    1502             : {
    1503           0 :     dumpRepresentationHeader(fp, indent,
    1504           0 :                              isFatInline() ? "JSFatInlineString" : "JSThinInlineString");
    1505           0 :     indent += 2;
    1506             : 
    1507           0 :     dumpRepresentationChars(fp, indent);
    1508           0 : }
    1509             : 
    1510             : void
    1511           0 : JSFlatString::dumpRepresentation(FILE* fp, int indent) const
    1512             : {
    1513           0 :     dumpRepresentationHeader(fp, indent, "JSFlatString");
    1514           0 :     indent += 2;
    1515             : 
    1516           0 :     dumpRepresentationChars(fp, indent);
    1517           0 : }
    1518             : #endif
    1519             : 
    1520             : static void
    1521             : FinalizeRepresentativeExternalString(const JSStringFinalizer* fin, char16_t* chars);
    1522             : 
    1523             : static const JSStringFinalizer RepresentativeExternalStringFinalizer =
    1524             :     { FinalizeRepresentativeExternalString };
    1525             : 
    1526             : static void
    1527           0 : FinalizeRepresentativeExternalString(const JSStringFinalizer* fin, char16_t* chars)
    1528             : {
    1529             :     // Constant chars, nothing to free.
    1530           0 :     MOZ_ASSERT(fin == &RepresentativeExternalStringFinalizer);
    1531           0 : }
    1532             : 
    1533             : template <typename CheckString, typename CharT>
    1534             : static bool
    1535           0 : FillWithRepresentatives(JSContext* cx, HandleArrayObject array, uint32_t* index,
    1536             :                         const CharT* chars, size_t len,
    1537             :                         size_t fatInlineMaxLength,
    1538             :                         const CheckString& check)
    1539             : {
    1540             :     auto AppendString =
    1541           0 :         [&check](JSContext* cx, HandleArrayObject array, uint32_t* index, HandleString s)
    1542           0 :     {
    1543           0 :         MOZ_ASSERT(check(s));
    1544             :         Unused << check; // silence clang -Wunused-lambda-capture in opt builds
    1545           0 :         RootedValue val(cx, StringValue(s));
    1546           0 :         return JS_DefineElement(cx, array, (*index)++, val, 0);
    1547           0 :     };
    1548             : 
    1549           0 :     MOZ_ASSERT(len > fatInlineMaxLength);
    1550             : 
    1551             :     // Normal atom.
    1552           0 :     RootedString atom1(cx, AtomizeChars(cx, chars, len));
    1553           0 :     if (!atom1 || !AppendString(cx, array, index, atom1))
    1554           0 :         return false;
    1555           0 :     MOZ_ASSERT(atom1->isAtom());
    1556             : 
    1557             :     // Inline atom.
    1558           0 :     RootedString atom2(cx, AtomizeChars(cx, chars, 2));
    1559           0 :     if (!atom2 || !AppendString(cx, array, index, atom2))
    1560           0 :         return false;
    1561           0 :     MOZ_ASSERT(atom2->isAtom());
    1562           0 :     MOZ_ASSERT(atom2->isInline());
    1563             : 
    1564             :     // Fat inline atom.
    1565           0 :     RootedString atom3(cx, AtomizeChars(cx, chars, fatInlineMaxLength));
    1566           0 :     if (!atom3 || !AppendString(cx, array, index, atom3))
    1567           0 :         return false;
    1568           0 :     MOZ_ASSERT(atom3->isAtom());
    1569           0 :     MOZ_ASSERT(atom3->isFatInline());
    1570             : 
    1571             :     // Normal flat string.
    1572           0 :     RootedString flat1(cx, NewStringCopyN<CanGC>(cx, chars, len));
    1573           0 :     if (!flat1 || !AppendString(cx, array, index, flat1))
    1574           0 :         return false;
    1575           0 :     MOZ_ASSERT(flat1->isFlat());
    1576             : 
    1577             :     // Inline string.
    1578           0 :     RootedString flat2(cx, NewStringCopyN<CanGC>(cx, chars, 3));
    1579           0 :     if (!flat2 || !AppendString(cx, array, index, flat2))
    1580           0 :         return false;
    1581           0 :     MOZ_ASSERT(flat2->isFlat());
    1582           0 :     MOZ_ASSERT(flat2->isInline());
    1583             : 
    1584             :     // Fat inline string.
    1585           0 :     RootedString flat3(cx, NewStringCopyN<CanGC>(cx, chars, fatInlineMaxLength));
    1586           0 :     if (!flat3 || !AppendString(cx, array, index, flat3))
    1587           0 :         return false;
    1588           0 :     MOZ_ASSERT(flat3->isFlat());
    1589           0 :     MOZ_ASSERT(flat3->isFatInline());
    1590             : 
    1591             :     // Rope.
    1592           0 :     RootedString rope(cx, ConcatStrings<CanGC>(cx, atom1, atom3));
    1593           0 :     if (!rope || !AppendString(cx, array, index, rope))
    1594           0 :         return false;
    1595           0 :     MOZ_ASSERT(rope->isRope());
    1596             : 
    1597             :     // Dependent.
    1598           0 :     RootedString dep(cx, NewDependentString(cx, atom1, 0, len - 2));
    1599           0 :     if (!dep || !AppendString(cx, array, index, dep))
    1600           0 :         return false;
    1601           0 :     MOZ_ASSERT(dep->isDependent());
    1602             : 
    1603             :     // Undepended.
    1604           0 :     RootedString undep(cx, NewDependentString(cx, atom1, 0, len - 3));
    1605           0 :     if (!undep || !undep->ensureFlat(cx) || !AppendString(cx, array, index, undep))
    1606           0 :         return false;
    1607           0 :     MOZ_ASSERT(undep->isUndepended());
    1608             : 
    1609             :     // Extensible.
    1610           0 :     RootedString temp1(cx, NewStringCopyN<CanGC>(cx, chars, len));
    1611           0 :     if (!temp1)
    1612           0 :         return false;
    1613           0 :     RootedString extensible(cx, ConcatStrings<CanGC>(cx, temp1, atom3));
    1614           0 :     if (!extensible || !extensible->ensureLinear(cx))
    1615           0 :         return false;
    1616           0 :     if (!AppendString(cx, array, index, extensible))
    1617           0 :         return false;
    1618           0 :     MOZ_ASSERT(extensible->isExtensible());
    1619             : 
    1620             :     // External. Note that we currently only support TwoByte external strings.
    1621           0 :     RootedString external1(cx), external2(cx);
    1622             :     if (IsSame<CharT, char16_t>::value) {
    1623           0 :         external1 = JS_NewExternalString(cx, (const char16_t*)chars, len,
    1624             :                                          &RepresentativeExternalStringFinalizer);
    1625           0 :         if (!external1 || !AppendString(cx, array, index, external1))
    1626           0 :             return false;
    1627           0 :         MOZ_ASSERT(external1->isExternal());
    1628             : 
    1629           0 :         external2 = JS_NewExternalString(cx, (const char16_t*)chars, 2,
    1630             :                                          &RepresentativeExternalStringFinalizer);
    1631           0 :         if (!external2 || !AppendString(cx, array, index, external2))
    1632           0 :             return false;
    1633           0 :         MOZ_ASSERT(external2->isExternal());
    1634             :     }
    1635             : 
    1636             :     // Assert the strings still have the types we expect after creating the
    1637             :     // other strings.
    1638             : 
    1639           0 :     MOZ_ASSERT(atom1->isAtom());
    1640           0 :     MOZ_ASSERT(atom2->isAtom());
    1641           0 :     MOZ_ASSERT(atom3->isAtom());
    1642           0 :     MOZ_ASSERT(atom2->isInline());
    1643           0 :     MOZ_ASSERT(atom3->isFatInline());
    1644             : 
    1645           0 :     MOZ_ASSERT(flat1->isFlat());
    1646           0 :     MOZ_ASSERT(flat2->isFlat());
    1647           0 :     MOZ_ASSERT(flat3->isFlat());
    1648           0 :     MOZ_ASSERT(flat2->isInline());
    1649           0 :     MOZ_ASSERT(flat3->isFatInline());
    1650             : 
    1651           0 :     MOZ_ASSERT(rope->isRope());
    1652           0 :     MOZ_ASSERT(dep->isDependent());
    1653           0 :     MOZ_ASSERT(undep->isUndepended());
    1654           0 :     MOZ_ASSERT(extensible->isExtensible());
    1655           0 :     MOZ_ASSERT_IF(external1, external1->isExternal());
    1656           0 :     MOZ_ASSERT_IF(external2, external2->isExternal());
    1657           0 :     return true;
    1658             : }
    1659             : 
    1660             : /* static */ bool
    1661           0 : JSString::fillWithRepresentatives(JSContext* cx, HandleArrayObject array)
    1662             : {
    1663           0 :     uint32_t index = 0;
    1664             : 
    1665           0 :     auto CheckTwoByte = [](JSString* str) { return str->hasTwoByteChars(); };
    1666           0 :     auto CheckLatin1 = [](JSString* str) { return str->hasLatin1Chars(); };
    1667             : 
    1668             :     // Append TwoByte strings.
    1669             :     static const char16_t twoByteChars[] = u"\u1234abc\0def\u5678ghijklmasdfa\0xyz0123456789";
    1670           0 :     if (!FillWithRepresentatives(cx, array, &index,
    1671           0 :                                  twoByteChars, mozilla::ArrayLength(twoByteChars) - 1,
    1672             :                                  JSFatInlineString::MAX_LENGTH_TWO_BYTE,
    1673             :                                  CheckTwoByte))
    1674             :     {
    1675           0 :         return false;
    1676             :     }
    1677             : 
    1678             :     // Append Latin1 strings.
    1679             :     static const Latin1Char latin1Chars[] = "abc\0defghijklmasdfa\0xyz0123456789";
    1680           0 :     if (!FillWithRepresentatives(cx, array, &index,
    1681           0 :                                  latin1Chars, mozilla::ArrayLength(latin1Chars) - 1,
    1682             :                                  JSFatInlineString::MAX_LENGTH_LATIN1,
    1683             :                                  CheckLatin1))
    1684             :     {
    1685           0 :         return false;
    1686             :     }
    1687             : 
    1688           0 :     MOZ_ASSERT(index == 22);
    1689           0 :     return true;
    1690             : }

Generated by: LCOV version 1.13