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 : }
|