Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / vm / StringType-inl.h
blobb16337c5936e4bb32412b608be6b92eb991f23b0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef vm_StringType_inl_h
8 #define vm_StringType_inl_h
10 #include "vm/StringType.h"
12 #include "mozilla/PodOperations.h"
13 #include "mozilla/Range.h"
15 #include "gc/GCEnum.h"
16 #include "gc/MaybeRooted.h"
17 #include "gc/StoreBuffer.h"
18 #include "js/UniquePtr.h"
19 #include "vm/StaticStrings.h"
21 #include "gc/GCContext-inl.h"
22 #include "gc/StoreBuffer-inl.h"
23 #include "vm/JSContext-inl.h"
25 namespace js {
27 // Allocate a thin inline string if possible, and a fat inline string if not.
28 template <AllowGC allowGC, typename CharT>
29 static MOZ_ALWAYS_INLINE JSInlineString* AllocateInlineString(
30 JSContext* cx, size_t len, CharT** chars, js::gc::Heap heap) {
31 MOZ_ASSERT(JSInlineString::lengthFits<CharT>(len));
33 if (JSThinInlineString::lengthFits<CharT>(len)) {
34 return cx->newCell<JSThinInlineString, allowGC>(heap, len, chars);
36 return cx->newCell<JSFatInlineString, allowGC>(heap, len, chars);
39 template <typename CharT>
40 static MOZ_ALWAYS_INLINE JSAtom* AllocateInlineAtom(JSContext* cx, size_t len,
41 CharT** chars,
42 js::HashNumber hash) {
43 MOZ_ASSERT(JSInlineString::lengthFits<CharT>(len));
45 if (JSThinInlineString::lengthFits<CharT>(len)) {
46 return cx->newCell<js::NormalAtom, js::NoGC>(len, chars, hash);
48 return cx->newCell<js::FatInlineAtom, js::NoGC>(len, chars, hash);
51 // Create a thin inline string if possible, and a fat inline string if not.
52 template <AllowGC allowGC, typename CharT>
53 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(
54 JSContext* cx, mozilla::Range<const CharT> chars,
55 js::gc::Heap heap = js::gc::Heap::Default) {
57 * Don't bother trying to find a static atom; measurement shows that not
58 * many get here (for one, Atomize is catching them).
61 size_t len = chars.length();
62 CharT* storage;
63 JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage, heap);
64 if (!str) {
65 return nullptr;
68 mozilla::PodCopy(storage, chars.begin().get(), len);
69 return str;
72 // Create a thin inline string if possible, and a fat inline string if not.
73 template <AllowGC allowGC, typename CharT, size_t N>
74 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(
75 JSContext* cx, const CharT (&chars)[N], size_t len,
76 js::gc::Heap heap = js::gc::Heap::Default) {
77 MOZ_ASSERT(len <= N);
80 * Don't bother trying to find a static atom; measurement shows that not
81 * many get here (for one, Atomize is catching them).
84 CharT* storage;
85 JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage, heap);
86 if (!str) {
87 return nullptr;
90 if (JSThinInlineString::lengthFits<CharT>(len)) {
91 constexpr size_t MaxLength = std::is_same_v<CharT, Latin1Char>
92 ? JSThinInlineString::MAX_LENGTH_LATIN1
93 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
95 // memcpy with a constant length can be optimized more easily by compilers.
96 constexpr size_t toCopy = std::min(N, MaxLength) * sizeof(CharT);
97 std::memcpy(storage, chars, toCopy);
98 } else {
99 constexpr size_t MaxLength = std::is_same_v<CharT, Latin1Char>
100 ? JSFatInlineString::MAX_LENGTH_LATIN1
101 : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
103 // memcpy with a constant length can be optimized more easily by compilers.
104 constexpr size_t toCopy = std::min(N, MaxLength) * sizeof(CharT);
105 std::memcpy(storage, chars, toCopy);
107 return str;
110 template <typename CharT>
111 static MOZ_ALWAYS_INLINE JSAtom* NewInlineAtom(JSContext* cx,
112 const CharT* chars,
113 size_t length,
114 js::HashNumber hash) {
115 CharT* storage;
116 JSAtom* str = AllocateInlineAtom(cx, length, &storage, hash);
117 if (!str) {
118 return nullptr;
121 mozilla::PodCopy(storage, chars, length);
122 return str;
125 // Create a thin inline string if possible, and a fat inline string if not.
126 template <typename CharT>
127 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineString(
128 JSContext* cx, Handle<JSLinearString*> base, size_t start, size_t length,
129 js::gc::Heap heap) {
130 MOZ_ASSERT(JSInlineString::lengthFits<CharT>(length));
132 CharT* chars;
133 JSInlineString* s = AllocateInlineString<CanGC>(cx, length, &chars, heap);
134 if (!s) {
135 return nullptr;
138 JS::AutoCheckCannotGC nogc;
139 mozilla::PodCopy(chars, base->chars<CharT>(nogc) + start, length);
140 return s;
143 template <typename CharT>
144 static MOZ_ALWAYS_INLINE JSLinearString* TryEmptyOrStaticString(
145 JSContext* cx, const CharT* chars, size_t n) {
146 // Measurements on popular websites indicate empty strings are pretty common
147 // and most strings with length 1 or 2 are in the StaticStrings table. For
148 // length 3 strings that's only about 1%, so we check n <= 2.
149 if (n <= 2) {
150 if (n == 0) {
151 return cx->emptyString();
154 if (JSLinearString* str = cx->staticStrings().lookup(chars, n)) {
155 return str;
159 return nullptr;
162 } /* namespace js */
164 MOZ_ALWAYS_INLINE bool JSString::validateLength(JSContext* maybecx,
165 size_t length) {
166 return validateLengthInternal<js::CanGC>(maybecx, length);
169 template <js::AllowGC allowGC>
170 MOZ_ALWAYS_INLINE bool JSString::validateLengthInternal(JSContext* maybecx,
171 size_t length) {
172 if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) {
173 if constexpr (allowGC) {
174 js::ReportOversizedAllocation(maybecx, JSMSG_ALLOC_OVERFLOW);
176 return false;
179 return true;
182 template <>
183 MOZ_ALWAYS_INLINE const char16_t* JSString::nonInlineCharsRaw() const {
184 return d.s.u2.nonInlineCharsTwoByte;
187 template <>
188 MOZ_ALWAYS_INLINE const JS::Latin1Char* JSString::nonInlineCharsRaw() const {
189 return d.s.u2.nonInlineCharsLatin1;
192 inline JSRope::JSRope(JSString* left, JSString* right, size_t length) {
193 // JITs expect rope children aren't empty.
194 MOZ_ASSERT(!left->empty() && !right->empty());
196 // |length| must be the sum of the length of both child nodes.
197 MOZ_ASSERT(left->length() + right->length() == length);
199 bool isLatin1 = left->hasLatin1Chars() && right->hasLatin1Chars();
201 // Do not try to make a rope that could fit inline.
202 MOZ_ASSERT_IF(!isLatin1, !JSInlineString::lengthFits<char16_t>(length));
203 MOZ_ASSERT_IF(isLatin1, !JSInlineString::lengthFits<JS::Latin1Char>(length));
205 if (isLatin1) {
206 setLengthAndFlags(length, INIT_ROPE_FLAGS | LATIN1_CHARS_BIT);
207 } else {
208 setLengthAndFlags(length, INIT_ROPE_FLAGS);
210 d.s.u2.left = left;
211 d.s.u3.right = right;
213 // Post-barrier by inserting into the whole cell buffer if either
214 // this -> left or this -> right is a tenured -> nursery edge.
215 if (isTenured()) {
216 js::gc::StoreBuffer* sb = left->storeBuffer();
217 if (!sb) {
218 sb = right->storeBuffer();
220 if (sb) {
221 sb->putWholeCell(this);
226 template <js::AllowGC allowGC>
227 MOZ_ALWAYS_INLINE JSRope* JSRope::new_(
228 JSContext* cx,
229 typename js::MaybeRooted<JSString*, allowGC>::HandleType left,
230 typename js::MaybeRooted<JSString*, allowGC>::HandleType right,
231 size_t length, js::gc::Heap heap) {
232 if (MOZ_UNLIKELY(!validateLengthInternal<allowGC>(cx, length))) {
233 return nullptr;
235 return cx->newCell<JSRope, allowGC>(heap, left, right, length);
238 inline JSDependentString::JSDependentString(JSLinearString* base, size_t start,
239 size_t length) {
240 MOZ_ASSERT(start + length <= base->length());
241 JS::AutoCheckCannotGC nogc;
242 if (base->hasLatin1Chars()) {
243 setLengthAndFlags(length, INIT_DEPENDENT_FLAGS | LATIN1_CHARS_BIT);
244 d.s.u2.nonInlineCharsLatin1 = base->latin1Chars(nogc) + start;
245 } else {
246 setLengthAndFlags(length, INIT_DEPENDENT_FLAGS);
247 d.s.u2.nonInlineCharsTwoByte = base->twoByteChars(nogc) + start;
249 d.s.u3.base = base;
250 if (isTenured() && !base->isTenured()) {
251 base->storeBuffer()->putWholeCell(this);
255 MOZ_ALWAYS_INLINE JSLinearString* JSDependentString::new_(
256 JSContext* cx, JSLinearString* baseArg, size_t start, size_t length,
257 js::gc::Heap heap) {
258 // Do not try to make a dependent string that could fit inline.
259 MOZ_ASSERT_IF(baseArg->hasTwoByteChars(),
260 !JSInlineString::lengthFits<char16_t>(length));
261 MOZ_ASSERT_IF(!baseArg->hasTwoByteChars(),
262 !JSInlineString::lengthFits<JS::Latin1Char>(length));
265 * Try to avoid long chains of dependent strings. We can't avoid these
266 * entirely, however, due to how ropes are flattened.
268 if (baseArg->isDependent()) {
269 start += baseArg->asDependent().baseOffset();
270 baseArg = baseArg->asDependent().base();
273 MOZ_ASSERT(start + length <= baseArg->length());
275 JSDependentString* str =
276 cx->newCell<JSDependentString, js::NoGC>(heap, baseArg, start, length);
277 if (str) {
278 return str;
281 JS::Rooted<JSLinearString*> base(cx, baseArg);
282 return cx->newCell<JSDependentString>(heap, base, start, length);
285 inline JSLinearString::JSLinearString(const char16_t* chars, size_t length) {
286 setLengthAndFlags(length, INIT_LINEAR_FLAGS);
287 // Check that the new buffer is located in the StringBufferArena
288 checkStringCharsArena(chars);
289 d.s.u2.nonInlineCharsTwoByte = chars;
292 inline JSLinearString::JSLinearString(const JS::Latin1Char* chars,
293 size_t length) {
294 setLengthAndFlags(length, INIT_LINEAR_FLAGS | LATIN1_CHARS_BIT);
295 // Check that the new buffer is located in the StringBufferArena
296 checkStringCharsArena(chars);
297 d.s.u2.nonInlineCharsLatin1 = chars;
300 void JSLinearString::disownCharsBecauseError() {
301 setLengthAndFlags(0, INIT_LINEAR_FLAGS | LATIN1_CHARS_BIT);
302 d.s.u2.nonInlineCharsLatin1 = nullptr;
305 template <js::AllowGC allowGC, typename CharT>
306 MOZ_ALWAYS_INLINE JSLinearString* JSLinearString::new_(
307 JSContext* cx, js::UniquePtr<CharT[], JS::FreePolicy> chars, size_t length,
308 js::gc::Heap heap) {
309 if (MOZ_UNLIKELY(!validateLengthInternal<allowGC>(cx, length))) {
310 return nullptr;
313 return newValidLength<allowGC>(cx, std::move(chars), length, heap);
316 template <js::AllowGC allowGC, typename CharT>
317 MOZ_ALWAYS_INLINE JSLinearString* JSLinearString::newValidLength(
318 JSContext* cx, js::UniquePtr<CharT[], JS::FreePolicy> chars, size_t length,
319 js::gc::Heap heap) {
320 MOZ_ASSERT(!cx->zone()->isAtomsZone());
321 MOZ_ASSERT(!JSInlineString::lengthFits<CharT>(length));
323 JSLinearString* str =
324 cx->newCell<JSLinearString, allowGC>(heap, chars.get(), length);
325 if (!str) {
326 return nullptr;
329 if (!str->isTenured()) {
330 // If the following registration fails, the string is partially initialized
331 // and must be made valid, or its finalizer may attempt to free
332 // uninitialized memory.
333 if (!cx->runtime()->gc.nursery().registerMallocedBuffer(
334 chars.get(), length * sizeof(CharT))) {
335 str->disownCharsBecauseError();
336 if (allowGC) {
337 ReportOutOfMemory(cx);
339 return nullptr;
341 } else {
342 cx->zone()->addCellMemory(str, length * sizeof(CharT),
343 js::MemoryUse::StringContents);
346 (void)chars.release();
347 return str;
350 template <typename CharT>
351 MOZ_ALWAYS_INLINE JSAtom* JSAtom::newValidLength(
352 JSContext* cx, js::UniquePtr<CharT[], JS::FreePolicy> chars, size_t length,
353 js::HashNumber hash) {
354 MOZ_ASSERT(validateLength(cx, length));
355 MOZ_ASSERT(cx->zone()->isAtomsZone());
356 JSAtom* str =
357 cx->newCell<js::NormalAtom, js::NoGC>(chars.get(), length, hash);
358 if (!str) {
359 return nullptr;
361 (void)chars.release();
363 MOZ_ASSERT(str->isTenured());
364 cx->zone()->addCellMemory(str, length * sizeof(CharT),
365 js::MemoryUse::StringContents);
367 return str;
370 inline js::PropertyName* JSLinearString::toPropertyName(JSContext* cx) {
371 #ifdef DEBUG
372 uint32_t dummy;
373 MOZ_ASSERT(!isIndex(&dummy));
374 #endif
375 if (isAtom()) {
376 return asAtom().asPropertyName();
378 JSAtom* atom = js::AtomizeString(cx, this);
379 if (!atom) {
380 return nullptr;
382 return atom->asPropertyName();
385 template <js::AllowGC allowGC>
386 MOZ_ALWAYS_INLINE JSThinInlineString* JSThinInlineString::new_(
387 JSContext* cx, js::gc::Heap heap) {
388 MOZ_ASSERT(!cx->zone()->isAtomsZone());
389 return cx->newCell<JSThinInlineString, allowGC>(heap);
392 template <js::AllowGC allowGC>
393 MOZ_ALWAYS_INLINE JSFatInlineString* JSFatInlineString::new_(
394 JSContext* cx, js::gc::Heap heap) {
395 MOZ_ASSERT(!cx->zone()->isAtomsZone());
396 return cx->newCell<JSFatInlineString, allowGC>(heap);
399 inline JSThinInlineString::JSThinInlineString(size_t length,
400 JS::Latin1Char** chars) {
401 MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
402 setLengthAndFlags(length, INIT_THIN_INLINE_FLAGS | LATIN1_CHARS_BIT);
403 *chars = d.inlineStorageLatin1;
406 inline JSThinInlineString::JSThinInlineString(size_t length, char16_t** chars) {
407 MOZ_ASSERT(lengthFits<char16_t>(length));
408 setLengthAndFlags(length, INIT_THIN_INLINE_FLAGS);
409 *chars = d.inlineStorageTwoByte;
412 inline JSFatInlineString::JSFatInlineString(size_t length,
413 JS::Latin1Char** chars) {
414 MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
415 setLengthAndFlags(length, INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT);
416 *chars = d.inlineStorageLatin1;
419 inline JSFatInlineString::JSFatInlineString(size_t length, char16_t** chars) {
420 MOZ_ASSERT(lengthFits<char16_t>(length));
421 setLengthAndFlags(length, INIT_FAT_INLINE_FLAGS);
422 *chars = d.inlineStorageTwoByte;
425 inline JSExternalString::JSExternalString(
426 const char16_t* chars, size_t length,
427 const JSExternalStringCallbacks* callbacks) {
428 MOZ_ASSERT(callbacks);
429 setLengthAndFlags(length, EXTERNAL_FLAGS);
430 d.s.u2.nonInlineCharsTwoByte = chars;
431 d.s.u3.externalCallbacks = callbacks;
434 MOZ_ALWAYS_INLINE JSExternalString* JSExternalString::new_(
435 JSContext* cx, const char16_t* chars, size_t length,
436 const JSExternalStringCallbacks* callbacks) {
437 if (MOZ_UNLIKELY(!validateLength(cx, length))) {
438 return nullptr;
440 auto* str = cx->newCell<JSExternalString>(chars, length, callbacks);
441 if (!str) {
442 return nullptr;
444 size_t nbytes = length * sizeof(char16_t);
446 MOZ_ASSERT(str->isTenured());
447 js::AddCellMemory(str, nbytes, js::MemoryUse::StringContents);
449 return str;
452 inline js::NormalAtom::NormalAtom(size_t length, JS::Latin1Char** chars,
453 js::HashNumber hash)
454 : hash_(hash) {
455 MOZ_ASSERT(JSInlineString::lengthFits<JS::Latin1Char>(length));
456 setLengthAndFlags(length,
457 INIT_THIN_INLINE_FLAGS | LATIN1_CHARS_BIT | ATOM_BIT);
458 *chars = d.inlineStorageLatin1;
461 inline js::NormalAtom::NormalAtom(size_t length, char16_t** chars,
462 js::HashNumber hash)
463 : hash_(hash) {
464 MOZ_ASSERT(JSInlineString::lengthFits<char16_t>(length));
465 setLengthAndFlags(length, INIT_THIN_INLINE_FLAGS | ATOM_BIT);
466 *chars = d.inlineStorageTwoByte;
469 inline js::NormalAtom::NormalAtom(const char16_t* chars, size_t length,
470 js::HashNumber hash)
471 : hash_(hash) {
472 setLengthAndFlags(length, INIT_LINEAR_FLAGS | ATOM_BIT);
473 // Check that the new buffer is located in the StringBufferArena
474 checkStringCharsArena(chars);
475 d.s.u2.nonInlineCharsTwoByte = chars;
478 inline js::NormalAtom::NormalAtom(const JS::Latin1Char* chars, size_t length,
479 js::HashNumber hash)
480 : hash_(hash) {
481 setLengthAndFlags(length, INIT_LINEAR_FLAGS | LATIN1_CHARS_BIT | ATOM_BIT);
482 // Check that the new buffer is located in the StringBufferArena
483 checkStringCharsArena(chars);
484 d.s.u2.nonInlineCharsLatin1 = chars;
487 inline js::FatInlineAtom::FatInlineAtom(size_t length, JS::Latin1Char** chars,
488 js::HashNumber hash)
489 : hash_(hash) {
490 MOZ_ASSERT(JSFatInlineString::lengthFits<JS::Latin1Char>(length));
491 setLengthAndFlags(length,
492 INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT | ATOM_BIT);
493 *chars = d.inlineStorageLatin1;
496 inline js::FatInlineAtom::FatInlineAtom(size_t length, char16_t** chars,
497 js::HashNumber hash)
498 : hash_(hash) {
499 MOZ_ASSERT(JSFatInlineString::lengthFits<char16_t>(length));
500 setLengthAndFlags(length, INIT_FAT_INLINE_FLAGS | ATOM_BIT);
501 *chars = d.inlineStorageTwoByte;
504 inline JSLinearString* js::StaticStrings::getUnitString(JSContext* cx,
505 char16_t c) {
506 if (c < UNIT_STATIC_LIMIT) {
507 return getUnit(c);
509 return js::NewInlineString<CanGC>(cx, {c}, 1);
512 inline JSLinearString* js::StaticStrings::getUnitStringForElement(
513 JSContext* cx, JSString* str, size_t index) {
514 MOZ_ASSERT(index < str->length());
516 char16_t c;
517 if (!str->getChar(cx, index, &c)) {
518 return nullptr;
520 return getUnitString(cx, c);
523 inline JSLinearString* js::StaticStrings::getUnitStringForElement(
524 JSContext* cx, JSLinearString* str, size_t index) {
525 MOZ_ASSERT(index < str->length());
527 char16_t c = str->latin1OrTwoByteChar(index);
528 return getUnitString(cx, c);
531 MOZ_ALWAYS_INLINE void JSString::finalize(JS::GCContext* gcx) {
532 /* FatInline strings are in a different arena. */
533 MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
534 MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
536 if (isLinear()) {
537 asLinear().finalize(gcx);
538 } else {
539 MOZ_ASSERT(isRope());
543 inline void JSLinearString::finalize(JS::GCContext* gcx) {
544 MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
545 MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
547 if (!isInline() && !isDependent()) {
548 gcx->free_(this, nonInlineCharsRaw(), allocSize(),
549 js::MemoryUse::StringContents);
553 inline void JSFatInlineString::finalize(JS::GCContext* gcx) {
554 MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_STRING);
555 MOZ_ASSERT(isInline());
557 // Nothing to do.
560 inline void js::FatInlineAtom::finalize(JS::GCContext* gcx) {
561 MOZ_ASSERT(JSString::isAtom());
562 MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_ATOM);
564 // Nothing to do.
567 inline void JSExternalString::finalize(JS::GCContext* gcx) {
568 MOZ_ASSERT(JSString::isExternal());
570 size_t nbytes = length() * sizeof(char16_t);
571 gcx->removeCellMemory(this, nbytes, js::MemoryUse::StringContents);
573 callbacks()->finalize(const_cast<char16_t*>(rawTwoByteChars()));
576 #endif /* vm_StringType_inl_h */