ParserAtom.cpp (42898B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "frontend/ParserAtom.h" 8 9 #include "mozilla/TextUtils.h" // mozilla::IsAscii 10 11 #include <memory> // std::uninitialized_fill_n 12 13 #include "jsnum.h" // CharsToNumber 14 15 #include "frontend/CompilationStencil.h" 16 #include "js/GCAPI.h" // JS::AutoSuppressGCAnalysis 17 #include "js/Printer.h" // Sprinter, QuoteString 18 #include "util/Identifier.h" // IsIdentifier 19 #include "util/StringBuilder.h" // StringBuilder 20 #include "util/Text.h" // AsciiDigitToNumber 21 #include "util/Unicode.h" 22 #include "vm/JSContext.h" 23 #include "vm/Runtime.h" 24 #include "vm/SelfHosting.h" // ExtendedUnclonedSelfHostedFunctionNamePrefix 25 #include "vm/StaticStrings.h" 26 #include "vm/StringType.h" 27 28 using namespace js; 29 using namespace js::frontend; 30 31 namespace js { 32 namespace frontend { 33 34 JSAtom* GetWellKnownAtom(JSContext* cx, WellKnownAtomId atomId) { 35 #define ASSERT_OFFSET_(NAME, _) \ 36 static_assert(offsetof(JSAtomState, NAME) == \ 37 int32_t(WellKnownAtomId::NAME) * \ 38 sizeof(js::ImmutableTenuredPtr<PropertyName*>)); 39 FOR_EACH_COMMON_PROPERTYNAME(ASSERT_OFFSET_); 40 #undef ASSERT_OFFSET_ 41 42 #define ASSERT_OFFSET_(NAME, _) \ 43 static_assert(offsetof(JSAtomState, NAME) == \ 44 int32_t(WellKnownAtomId::NAME) * \ 45 sizeof(js::ImmutableTenuredPtr<PropertyName*>)); 46 JS_FOR_EACH_PROTOTYPE(ASSERT_OFFSET_); 47 #undef ASSERT_OFFSET_ 48 49 #define ASSERT_OFFSET_(NAME) \ 50 static_assert(offsetof(JSAtomState, NAME) == \ 51 int32_t(WellKnownAtomId::NAME) * \ 52 sizeof(js::ImmutableTenuredPtr<PropertyName*>)); 53 JS_FOR_EACH_WELL_KNOWN_SYMBOL(ASSERT_OFFSET_); 54 #undef ASSERT_OFFSET_ 55 56 static_assert(int32_t(WellKnownAtomId::abort) == 0, 57 "Unexpected order of WellKnownAtom"); 58 59 return (&cx->names().abort)[int32_t(atomId)]; 60 } 61 62 #ifdef DEBUG 63 void TaggedParserAtomIndex::validateRaw() { 64 if (isParserAtomIndex()) { 65 MOZ_ASSERT(toParserAtomIndex().index < IndexLimit); 66 } else if (isWellKnownAtomId()) { 67 MOZ_ASSERT(uint32_t(toWellKnownAtomId()) < 68 uint32_t(WellKnownAtomId::Limit)); 69 } else if (isLength1StaticParserString()) { 70 // always valid 71 } else if (isLength2StaticParserString()) { 72 MOZ_ASSERT(size_t(toLength2StaticParserString()) < Length2StaticLimit); 73 } else if (isLength3StaticParserString()) { 74 // always valid 75 } else { 76 MOZ_ASSERT(isNull()); 77 } 78 } 79 #endif 80 81 HashNumber TaggedParserAtomIndex::staticOrWellKnownHash() const { 82 MOZ_ASSERT(!isParserAtomIndex()); 83 84 if (isWellKnownAtomId()) { 85 const auto& info = GetWellKnownAtomInfo(toWellKnownAtomId()); 86 return info.hash; 87 } 88 89 if (isLength1StaticParserString()) { 90 Latin1Char content[1]; 91 ParserAtomsTable::getLength1Content(toLength1StaticParserString(), content); 92 return mozilla::HashString(content, 1); 93 } 94 95 if (isLength2StaticParserString()) { 96 char content[2]; 97 ParserAtomsTable::getLength2Content(toLength2StaticParserString(), content); 98 return mozilla::HashString(reinterpret_cast<const Latin1Char*>(content), 2); 99 } 100 101 MOZ_ASSERT(isLength3StaticParserString()); 102 char content[3]; 103 ParserAtomsTable::getLength3Content(toLength3StaticParserString(), content); 104 return mozilla::HashString(reinterpret_cast<const Latin1Char*>(content), 3); 105 } 106 107 template <typename CharT, typename SeqCharT> 108 /* static */ ParserAtom* ParserAtom::allocate( 109 FrontendContext* fc, LifoAlloc& alloc, InflatedChar16Sequence<SeqCharT> seq, 110 uint32_t length, HashNumber hash) { 111 constexpr size_t HeaderSize = sizeof(ParserAtom); 112 void* raw = alloc.alloc(HeaderSize + (sizeof(CharT) * length)); 113 if (!raw) { 114 js::ReportOutOfMemory(fc); 115 return nullptr; 116 } 117 118 constexpr bool hasTwoByteChars = (sizeof(CharT) == 2); 119 static_assert(sizeof(CharT) == 1 || sizeof(CharT) == 2, 120 "CharT should be 1 or 2 byte type"); 121 ParserAtom* entry = new (raw) ParserAtom(length, hash, hasTwoByteChars); 122 CharT* entryBuf = entry->chars<CharT>(); 123 drainChar16Seq(entryBuf, seq, length); 124 return entry; 125 } 126 127 bool ParserAtom::isInstantiatedAsJSAtom() const { 128 if (isMarkedAtomize()) { 129 return true; 130 } 131 132 // Always use JSAtom for short strings. 133 if (length() < MinimumLengthForNonAtom) { 134 return true; 135 } 136 137 return false; 138 } 139 140 JSString* ParserAtom::instantiateString(JSContext* cx, FrontendContext* fc, 141 ParserAtomIndex index, 142 CompilationAtomCache& atomCache) const { 143 MOZ_ASSERT(!isInstantiatedAsJSAtom()); 144 145 JSString* str; 146 if (hasLatin1Chars()) { 147 str = NewStringCopyNDontDeflateNonStaticValidLength<CanGC>( 148 cx, latin1Chars(), length(), gc::Heap::Tenured); 149 } else { 150 str = NewStringCopyNDontDeflateNonStaticValidLength<CanGC>( 151 cx, twoByteChars(), length(), gc::Heap::Tenured); 152 } 153 if (!str) { 154 return nullptr; 155 } 156 if (!atomCache.setAtomAt(fc, index, str)) { 157 return nullptr; 158 } 159 160 return str; 161 } 162 163 JSAtom* ParserAtom::instantiateAtom(JSContext* cx, FrontendContext* fc, 164 ParserAtomIndex index, 165 CompilationAtomCache& atomCache) const { 166 MOZ_ASSERT(isInstantiatedAsJSAtom()); 167 168 JSAtom* atom; 169 if (hasLatin1Chars()) { 170 atom = 171 AtomizeCharsNonStaticValidLength(cx, hash(), latin1Chars(), length()); 172 } else { 173 atom = 174 AtomizeCharsNonStaticValidLength(cx, hash(), twoByteChars(), length()); 175 } 176 if (!atom) { 177 return nullptr; 178 } 179 if (!atomCache.setAtomAt(fc, index, atom)) { 180 return nullptr; 181 } 182 return atom; 183 } 184 185 JSAtom* ParserAtom::instantiatePermanentAtom( 186 JSContext* cx, FrontendContext* fc, AtomSet& atomSet, ParserAtomIndex index, 187 CompilationAtomCache& atomCache) const { 188 MOZ_ASSERT(!cx->zone()); 189 190 MOZ_ASSERT(hasLatin1Chars()); 191 MOZ_ASSERT(length() <= JSString::MAX_LENGTH); 192 JSAtom* atom = PermanentlyAtomizeCharsNonStaticValidLength( 193 cx, atomSet, hash(), latin1Chars(), length()); 194 if (!atom) { 195 return nullptr; 196 } 197 if (!atomCache.setAtomAt(fc, index, atom)) { 198 return nullptr; 199 } 200 return atom; 201 } 202 203 #if defined(DEBUG) || defined(JS_JITSPEW) 204 void ParserAtom::dump() const { 205 js::Fprinter out(stderr); 206 out.put("\""); 207 dumpCharsNoQuote(out); 208 out.put("\"\n"); 209 } 210 211 void ParserAtom::dumpCharsNoQuote(js::GenericPrinter& out) const { 212 if (hasLatin1Chars()) { 213 JSString::dumpCharsNoQuote<Latin1Char>(latin1Chars(), length(), out); 214 } else { 215 JSString::dumpCharsNoQuote<char16_t>(twoByteChars(), length(), out); 216 } 217 } 218 219 void ParserAtomsTable::dump(TaggedParserAtomIndex index) const { 220 if (index.isParserAtomIndex()) { 221 getParserAtom(index.toParserAtomIndex())->dump(); 222 return; 223 } 224 225 if (index.isWellKnownAtomId()) { 226 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 227 js::Fprinter out(stderr); 228 out.put("\""); 229 out.put(info.content, info.length); 230 out.put("\""); 231 return; 232 } 233 234 if (index.isLength1StaticParserString()) { 235 js::Fprinter out(stderr); 236 out.put("\""); 237 dumpCharsNoQuote(out, index.toLength1StaticParserString()); 238 out.put("\"\n"); 239 return; 240 } 241 242 if (index.isLength2StaticParserString()) { 243 js::Fprinter out(stderr); 244 out.put("\""); 245 dumpCharsNoQuote(out, index.toLength2StaticParserString()); 246 out.put("\"\n"); 247 return; 248 } 249 250 if (index.isLength3StaticParserString()) { 251 js::Fprinter out(stderr); 252 out.put("\""); 253 dumpCharsNoQuote(out, index.toLength3StaticParserString()); 254 out.put("\"\n"); 255 return; 256 } 257 258 MOZ_ASSERT(index.isNull()); 259 js::Fprinter out(stderr); 260 out.put("#<null>"); 261 } 262 263 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out, 264 TaggedParserAtomIndex index) const { 265 if (index.isParserAtomIndex()) { 266 getParserAtom(index.toParserAtomIndex())->dumpCharsNoQuote(out); 267 return; 268 } 269 270 if (index.isWellKnownAtomId()) { 271 dumpCharsNoQuote(out, index.toWellKnownAtomId()); 272 return; 273 } 274 275 if (index.isLength1StaticParserString()) { 276 dumpCharsNoQuote(out, index.toLength1StaticParserString()); 277 return; 278 } 279 280 if (index.isLength2StaticParserString()) { 281 dumpCharsNoQuote(out, index.toLength2StaticParserString()); 282 return; 283 } 284 285 if (index.isLength3StaticParserString()) { 286 dumpCharsNoQuote(out, index.toLength3StaticParserString()); 287 return; 288 } 289 290 MOZ_ASSERT(index.isNull()); 291 out.put("#<null>"); 292 } 293 294 /* static */ 295 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out, 296 WellKnownAtomId id) { 297 const auto& info = GetWellKnownAtomInfo(id); 298 out.put(info.content, info.length); 299 } 300 301 /* static */ 302 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out, 303 Length1StaticParserString index) { 304 Latin1Char content[1]; 305 getLength1Content(index, content); 306 out.putChar(content[0]); 307 } 308 309 /* static */ 310 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out, 311 Length2StaticParserString index) { 312 char content[2]; 313 getLength2Content(index, content); 314 out.putChar(content[0]); 315 out.putChar(content[1]); 316 } 317 318 /* static */ 319 void ParserAtomsTable::dumpCharsNoQuote(js::GenericPrinter& out, 320 Length3StaticParserString index) { 321 char content[3]; 322 getLength3Content(index, content); 323 out.putChar(content[0]); 324 out.putChar(content[1]); 325 out.putChar(content[2]); 326 } 327 #endif 328 329 ParserAtomsTable::ParserAtomsTable(LifoAlloc& alloc) : alloc_(&alloc) {} 330 331 TaggedParserAtomIndex ParserAtomsTable::addEntry(FrontendContext* fc, 332 EntryMap::AddPtr& addPtr, 333 ParserAtom* entry) { 334 MOZ_ASSERT(!addPtr); 335 ParserAtomIndex index = ParserAtomIndex(entries_.length()); 336 if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) { 337 ReportAllocationOverflow(fc); 338 return TaggedParserAtomIndex::null(); 339 } 340 if (!entries_.append(entry)) { 341 js::ReportOutOfMemory(fc); 342 return TaggedParserAtomIndex::null(); 343 } 344 auto taggedIndex = TaggedParserAtomIndex(index); 345 if (!entryMap_.add(addPtr, entry, taggedIndex)) { 346 js::ReportOutOfMemory(fc); 347 return TaggedParserAtomIndex::null(); 348 } 349 return taggedIndex; 350 } 351 352 template <typename AtomCharT, typename SeqCharT> 353 TaggedParserAtomIndex ParserAtomsTable::internChar16Seq( 354 FrontendContext* fc, EntryMap::AddPtr& addPtr, HashNumber hash, 355 InflatedChar16Sequence<SeqCharT> seq, uint32_t length) { 356 MOZ_ASSERT(!addPtr); 357 358 ParserAtom* entry = 359 ParserAtom::allocate<AtomCharT>(fc, *alloc_, seq, length, hash); 360 if (!entry) { 361 return TaggedParserAtomIndex::null(); 362 } 363 return addEntry(fc, addPtr, entry); 364 } 365 366 static const uint16_t MAX_LATIN1_CHAR = 0xff; 367 368 TaggedParserAtomIndex ParserAtomsTable::internAscii(FrontendContext* fc, 369 const char* asciiPtr, 370 uint32_t length) { 371 // ASCII strings are strict subsets of Latin1 strings. 372 const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(asciiPtr); 373 return internLatin1(fc, latin1Ptr, length); 374 } 375 376 TaggedParserAtomIndex ParserAtomsTable::internLatin1( 377 FrontendContext* fc, const Latin1Char* latin1Ptr, uint32_t length) { 378 // Check for tiny strings which are abundant in minified code. 379 if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndex( 380 latin1Ptr, length)) { 381 return tiny; 382 } 383 384 // Check for well-known atom. 385 InflatedChar16Sequence<Latin1Char> seq(latin1Ptr, length); 386 SpecificParserAtomLookup<Latin1Char> lookup(seq); 387 if (auto wk = WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup)) { 388 return wk; 389 } 390 391 // Check for existing atom. 392 auto addPtr = entryMap_.lookupForAdd(lookup); 393 if (addPtr) { 394 return addPtr->value(); 395 } 396 397 return internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq, length); 398 } 399 400 bool IsWide(const InflatedChar16Sequence<char16_t>& seq) { 401 InflatedChar16Sequence<char16_t> seqCopy = seq; 402 while (seqCopy.hasMore()) { 403 char16_t ch = seqCopy.next(); 404 if (ch > MAX_LATIN1_CHAR) { 405 return true; 406 } 407 } 408 409 return false; 410 } 411 412 template <typename AtomCharT> 413 TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtomImpl( 414 FrontendContext* fc, const ParserAtom* atom) { 415 InflatedChar16Sequence<AtomCharT> seq(atom->chars<AtomCharT>(), 416 atom->length()); 417 SpecificParserAtomLookup<AtomCharT> lookup(seq, atom->hash()); 418 419 // Check for existing atom. 420 auto addPtr = entryMap_.lookupForAdd(lookup); 421 if (addPtr) { 422 auto index = addPtr->value(); 423 424 // Copy UsedByStencilFlag and AtomizeFlag. 425 MOZ_ASSERT(entries_[index.toParserAtomIndex()]->hasTwoByteChars() == 426 atom->hasTwoByteChars()); 427 entries_[index.toParserAtomIndex()]->flags_ |= atom->flags_; 428 return index; 429 } 430 431 auto index = 432 internChar16Seq<AtomCharT>(fc, addPtr, atom->hash(), seq, atom->length()); 433 if (!index) { 434 return TaggedParserAtomIndex::null(); 435 } 436 437 // Copy UsedByStencilFlag and AtomizeFlag. 438 MOZ_ASSERT(entries_[index.toParserAtomIndex()]->hasTwoByteChars() == 439 atom->hasTwoByteChars()); 440 entries_[index.toParserAtomIndex()]->flags_ |= atom->flags_; 441 return index; 442 } 443 444 TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtom( 445 FrontendContext* fc, const ParserAtom* atom) { 446 if (atom->hasLatin1Chars()) { 447 return internExternalParserAtomImpl<JS::Latin1Char>(fc, atom); 448 } 449 return internExternalParserAtomImpl<char16_t>(fc, atom); 450 } 451 452 bool ParserAtomsTable::addPlaceholder(FrontendContext* fc) { 453 ParserAtomIndex index = ParserAtomIndex(entries_.length()); 454 if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) { 455 ReportAllocationOverflow(fc); 456 return false; 457 } 458 if (!entries_.append(nullptr)) { 459 js::ReportOutOfMemory(fc); 460 return false; 461 } 462 return true; 463 } 464 465 TaggedParserAtomIndex ParserAtomsTable::internExternalParserAtomIndex( 466 FrontendContext* fc, const CompilationStencil& context, 467 TaggedParserAtomIndex atom) { 468 // When the atom is not a parser atom index, the value represent the atom 469 // without the need for a ParserAtom, and thus we can skip interning it. 470 if (!atom.isParserAtomIndex()) { 471 return atom; 472 } 473 auto index = atom.toParserAtomIndex(); 474 return internExternalParserAtom(fc, context.parserAtomData[index]); 475 } 476 477 bool ParserAtomsTable::isEqualToExternalParserAtomIndex( 478 TaggedParserAtomIndex internal, const CompilationStencil& context, 479 TaggedParserAtomIndex external) const { 480 // If one is null, well-known or static, then testing the equality of the bits 481 // of the TaggedParserAtomIndex is sufficient. 482 if (!internal.isParserAtomIndex() || !external.isParserAtomIndex()) { 483 return internal == external; 484 } 485 486 // Otherwise we have to compare 2 atom-indexes from different ParserAtomTable. 487 ParserAtom* internalAtom = getParserAtom(internal.toParserAtomIndex()); 488 ParserAtom* externalAtom = 489 context.parserAtomData[external.toParserAtomIndex()]; 490 491 if (internalAtom->hash() != externalAtom->hash()) { 492 return false; 493 } 494 495 HashNumber hash = internalAtom->hash(); 496 size_t length = internalAtom->length(); 497 if (internalAtom->hasLatin1Chars()) { 498 const Latin1Char* chars = internalAtom->latin1Chars(); 499 InflatedChar16Sequence<Latin1Char> seq(chars, length); 500 return externalAtom->equalsSeq(hash, seq); 501 } 502 503 const char16_t* chars = internalAtom->twoByteChars(); 504 InflatedChar16Sequence<char16_t> seq(chars, length); 505 return externalAtom->equalsSeq(hash, seq); 506 } 507 508 bool ParserAtomSpanBuilder::allocate(FrontendContext* fc, LifoAlloc& alloc, 509 size_t count) { 510 if (count >= TaggedParserAtomIndex::IndexLimit) { 511 ReportAllocationOverflow(fc); 512 return false; 513 } 514 515 auto* p = alloc.newArrayUninitialized<ParserAtom*>(count); 516 if (!p) { 517 js::ReportOutOfMemory(fc); 518 return false; 519 } 520 std::uninitialized_fill_n(p, count, nullptr); 521 522 entries_ = mozilla::Span(p, count); 523 return true; 524 } 525 526 static inline bool IsLatin1(mozilla::Utf8Unit c1, mozilla::Utf8Unit c2) { 527 auto u1 = c1.toUint8(); 528 auto u2 = c2.toUint8(); 529 530 // 0x80-0xBF 531 if (u1 == 0xC2 && 0x80 <= u2 && u2 <= 0xBF) { 532 return true; 533 } 534 535 // 0xC0-0xFF 536 if (u1 == 0xC3 && 0x80 <= u2 && u2 <= 0xBF) { 537 return true; 538 } 539 540 return false; 541 } 542 543 TaggedParserAtomIndex ParserAtomsTable::internUtf8( 544 FrontendContext* fc, const mozilla::Utf8Unit* utf8Ptr, uint32_t nbyte) { 545 if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndexUTF8( 546 utf8Ptr, nbyte)) { 547 return tiny; 548 } 549 550 // If source text is ASCII, then the length of the target char buffer 551 // is the same as the length of the UTF8 input. Convert it to a Latin1 552 // encoded string on the heap. 553 JS::UTF8Chars utf8(utf8Ptr, nbyte); 554 JS::SmallestEncoding minEncoding = FindSmallestEncoding(utf8); 555 if (minEncoding == JS::SmallestEncoding::ASCII) { 556 // As ascii strings are a subset of Latin1 strings, and each encoding 557 // unit is the same size, we can reliably cast this `Utf8Unit*` 558 // to a `Latin1Char*`. 559 const Latin1Char* latin1Ptr = reinterpret_cast<const Latin1Char*>(utf8Ptr); 560 return internLatin1(fc, latin1Ptr, nbyte); 561 } 562 563 // Check for existing. 564 // NOTE: Well-known are all ASCII so have been handled above. 565 InflatedChar16Sequence<mozilla::Utf8Unit> seq(utf8Ptr, nbyte); 566 SpecificParserAtomLookup<mozilla::Utf8Unit> lookup(seq); 567 MOZ_ASSERT(!WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup)); 568 EntryMap::AddPtr addPtr = entryMap_.lookupForAdd(lookup); 569 if (addPtr) { 570 return addPtr->value(); 571 } 572 573 // Compute length in code-points. 574 uint32_t length = 0; 575 InflatedChar16Sequence<mozilla::Utf8Unit> seqCopy = seq; 576 while (seqCopy.hasMore()) { 577 (void)seqCopy.next(); 578 length += 1; 579 } 580 581 // Otherwise, add new entry. 582 bool wide = (minEncoding == JS::SmallestEncoding::UTF16); 583 return wide 584 ? internChar16Seq<char16_t>(fc, addPtr, lookup.hash(), seq, length) 585 : internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq, 586 length); 587 } 588 589 TaggedParserAtomIndex ParserAtomsTable::internChar16(FrontendContext* fc, 590 const char16_t* char16Ptr, 591 uint32_t length) { 592 // Check for tiny strings which are abundant in minified code. 593 if (auto tiny = WellKnownParserAtoms::getSingleton().lookupTinyIndex( 594 char16Ptr, length)) { 595 return tiny; 596 } 597 598 // Check against well-known. 599 InflatedChar16Sequence<char16_t> seq(char16Ptr, length); 600 SpecificParserAtomLookup<char16_t> lookup(seq); 601 if (auto wk = WellKnownParserAtoms::getSingleton().lookupChar16Seq(lookup)) { 602 return wk; 603 } 604 605 // Check for existing atom. 606 EntryMap::AddPtr addPtr = entryMap_.lookupForAdd(lookup); 607 if (addPtr) { 608 return addPtr->value(); 609 } 610 611 // Otherwise, add new entry. 612 return IsWide(seq) 613 ? internChar16Seq<char16_t>(fc, addPtr, lookup.hash(), seq, length) 614 : internChar16Seq<Latin1Char>(fc, addPtr, lookup.hash(), seq, 615 length); 616 } 617 618 TaggedParserAtomIndex ParserAtomsTable::internJSAtom( 619 FrontendContext* fc, CompilationAtomCache& atomCache, JSAtom* atom) { 620 TaggedParserAtomIndex parserAtom; 621 { 622 JS::AutoCheckCannotGC nogc; 623 624 parserAtom = 625 atom->hasLatin1Chars() 626 ? internLatin1(fc, atom->latin1Chars(nogc), atom->length()) 627 : internChar16(fc, atom->twoByteChars(nogc), atom->length()); 628 if (!parserAtom) { 629 return TaggedParserAtomIndex::null(); 630 } 631 } 632 633 if (parserAtom.isParserAtomIndex()) { 634 ParserAtomIndex index = parserAtom.toParserAtomIndex(); 635 if (!atomCache.hasAtomAt(index)) { 636 if (!atomCache.setAtomAt(fc, index, atom)) { 637 return TaggedParserAtomIndex::null(); 638 } 639 } 640 } 641 642 // We should (infallibly) map back to the same JSAtom. 643 #ifdef DEBUG 644 if (JSContext* cx = fc->maybeCurrentJSContext()) { 645 JS::AutoSuppressGCAnalysis suppress(cx); 646 MOZ_ASSERT(toJSAtom(cx, fc, parserAtom, atomCache) == atom); 647 } 648 #endif 649 650 return parserAtom; 651 } 652 653 ParserAtom* ParserAtomsTable::getParserAtom(ParserAtomIndex index) const { 654 return entries_[index]; 655 } 656 657 void ParserAtomsTable::markUsedByStencil(TaggedParserAtomIndex index, 658 ParserAtom::Atomize atomize) const { 659 if (!index.isParserAtomIndex()) { 660 return; 661 } 662 663 getParserAtom(index.toParserAtomIndex())->markUsedByStencil(atomize); 664 } 665 666 void ParserAtomsTable::markAtomize(TaggedParserAtomIndex index, 667 ParserAtom::Atomize atomize) const { 668 if (!index.isParserAtomIndex()) { 669 return; 670 } 671 672 getParserAtom(index.toParserAtomIndex())->markAtomize(atomize); 673 } 674 675 bool ParserAtomsTable::isIdentifier(TaggedParserAtomIndex index) const { 676 if (index.isParserAtomIndex()) { 677 const auto* atom = getParserAtom(index.toParserAtomIndex()); 678 return atom->hasLatin1Chars() 679 ? IsIdentifier(atom->latin1Chars(), atom->length()) 680 : IsIdentifier(atom->twoByteChars(), atom->length()); 681 } 682 683 if (index.isWellKnownAtomId()) { 684 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 685 return IsIdentifier(reinterpret_cast<const Latin1Char*>(info.content), 686 info.length); 687 } 688 689 if (index.isLength1StaticParserString()) { 690 Latin1Char content[1]; 691 getLength1Content(index.toLength1StaticParserString(), content); 692 if (MOZ_UNLIKELY(content[0] > 127)) { 693 return IsIdentifier(content, 1); 694 } 695 return IsIdentifierASCII(char(content[0])); 696 } 697 698 if (index.isLength2StaticParserString()) { 699 char content[2]; 700 getLength2Content(index.toLength2StaticParserString(), content); 701 return IsIdentifierASCII(content[0], content[1]); 702 } 703 704 MOZ_ASSERT(index.isLength3StaticParserString()); 705 #ifdef DEBUG 706 char content[3]; 707 getLength3Content(index.toLength3StaticParserString(), content); 708 MOZ_ASSERT(!reinterpret_cast<const Latin1Char*>( 709 IsIdentifier(reinterpret_cast<const Latin1Char*>(content), 3))); 710 #endif 711 return false; 712 } 713 714 bool ParserAtomsTable::isPrivateName(TaggedParserAtomIndex index) const { 715 if (!index.isParserAtomIndex()) { 716 return false; 717 } 718 719 const auto* atom = getParserAtom(index.toParserAtomIndex()); 720 return atom->isPrivateName(); 721 } 722 723 bool ParserAtomsTable::isExtendedUnclonedSelfHostedFunctionName( 724 TaggedParserAtomIndex index) const { 725 if (index.isParserAtomIndex()) { 726 const auto* atom = getParserAtom(index.toParserAtomIndex()); 727 if (atom->length() < 2) { 728 return false; 729 } 730 731 return atom->charAt(0) == ExtendedUnclonedSelfHostedFunctionNamePrefix; 732 } 733 734 if (index.isWellKnownAtomId()) { 735 switch (index.toWellKnownAtomId()) { 736 case WellKnownAtomId::dollar_ArrayBufferSpecies_: 737 case WellKnownAtomId::dollar_ArraySpecies_: 738 case WellKnownAtomId::dollar_ArrayValues_: 739 case WellKnownAtomId::dollar_RegExpFlagsGetter_: 740 case WellKnownAtomId::dollar_RegExpToString_: 741 case WellKnownAtomId::dollar_SharedArrayBufferSpecies_: 742 case WellKnownAtomId::dollar_TypedArraySpecies_: { 743 #ifdef DEBUG 744 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 745 MOZ_ASSERT(info.content[0] == 746 ExtendedUnclonedSelfHostedFunctionNamePrefix); 747 #endif 748 return true; 749 } 750 default: { 751 #ifdef DEBUG 752 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 753 MOZ_ASSERT(info.length == 0 || 754 info.content[0] != 755 ExtendedUnclonedSelfHostedFunctionNamePrefix); 756 #endif 757 break; 758 } 759 } 760 return false; 761 } 762 763 // Length-1/2/3 shouldn't be used for extented uncloned self-hosted 764 // function name, and this query shouldn't be used for them. 765 #ifdef DEBUG 766 if (index.isLength1StaticParserString()) { 767 Latin1Char content[1]; 768 getLength1Content(index.toLength1StaticParserString(), content); 769 MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix); 770 } else if (index.isLength2StaticParserString()) { 771 char content[2]; 772 getLength2Content(index.toLength2StaticParserString(), content); 773 MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix); 774 } else { 775 MOZ_ASSERT(index.isLength3StaticParserString()); 776 char content[3]; 777 getLength3Content(index.toLength3StaticParserString(), content); 778 MOZ_ASSERT(content[0] != ExtendedUnclonedSelfHostedFunctionNamePrefix); 779 } 780 #endif 781 return false; 782 } 783 784 static bool HasUnpairedSurrogate(mozilla::Range<const char16_t> chars) { 785 for (auto ptr = chars.begin(); ptr < chars.end();) { 786 char16_t ch = *ptr++; 787 if (unicode::IsLeadSurrogate(ch)) { 788 if (ptr == chars.end() || !unicode::IsTrailSurrogate(*ptr++)) { 789 return true; 790 } 791 } else if (unicode::IsTrailSurrogate(ch)) { 792 return true; 793 } 794 } 795 return false; 796 } 797 798 bool ParserAtomsTable::isModuleExportName(TaggedParserAtomIndex index) const { 799 if (index.isParserAtomIndex()) { 800 const ParserAtom* name = getParserAtom(index.toParserAtomIndex()); 801 return name->hasLatin1Chars() || 802 !HasUnpairedSurrogate(name->twoByteRange()); 803 } 804 805 // Well-known/length-2 are ASCII. 806 // length-1 are Latin1. 807 return true; 808 } 809 810 bool ParserAtomsTable::isIndex(TaggedParserAtomIndex index, 811 uint32_t* indexp) const { 812 if (index.isParserAtomIndex()) { 813 const auto* atom = getParserAtom(index.toParserAtomIndex()); 814 size_t len = atom->length(); 815 if (len == 0 || len > UINT32_CHAR_BUFFER_LENGTH) { 816 return false; 817 } 818 if (atom->hasLatin1Chars()) { 819 return mozilla::IsAsciiDigit(*atom->latin1Chars()) && 820 js::CheckStringIsIndex(atom->latin1Chars(), len, indexp); 821 } 822 return mozilla::IsAsciiDigit(*atom->twoByteChars()) && 823 js::CheckStringIsIndex(atom->twoByteChars(), len, indexp); 824 } 825 826 if (index.isWellKnownAtomId()) { 827 #ifdef DEBUG 828 // Well-known atom shouldn't start with digit. 829 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 830 MOZ_ASSERT(info.length == 0 || !mozilla::IsAsciiDigit(info.content[0])); 831 #endif 832 return false; 833 } 834 835 if (index.isLength1StaticParserString()) { 836 Latin1Char content[1]; 837 getLength1Content(index.toLength1StaticParserString(), content); 838 if (mozilla::IsAsciiDigit(content[0])) { 839 *indexp = AsciiDigitToNumber(content[0]); 840 return true; 841 } 842 return false; 843 } 844 845 if (index.isLength2StaticParserString()) { 846 char content[2]; 847 getLength2Content(index.toLength2StaticParserString(), content); 848 // Leading '0' isn't allowed. 849 // See CheckStringIsIndex comment. 850 if (content[0] != '0' && mozilla::IsAsciiDigit(content[0]) && 851 mozilla::IsAsciiDigit(content[1])) { 852 *indexp = 853 AsciiDigitToNumber(content[0]) * 10 + AsciiDigitToNumber(content[1]); 854 return true; 855 } 856 return false; 857 } 858 859 MOZ_ASSERT(index.isLength3StaticParserString()); 860 *indexp = uint32_t(index.toLength3StaticParserString()); 861 #ifdef DEBUG 862 char content[3]; 863 getLength3Content(index.toLength3StaticParserString(), content); 864 MOZ_ASSERT(uint32_t(AsciiDigitToNumber(content[0])) * 100 + 865 uint32_t(AsciiDigitToNumber(content[1])) * 10 + 866 uint32_t(AsciiDigitToNumber(content[2])) == 867 *indexp); 868 MOZ_ASSERT(100 <= *indexp); 869 #endif 870 return true; 871 } 872 873 bool ParserAtomsTable::isInstantiatedAsJSAtom( 874 TaggedParserAtomIndex index) const { 875 if (index.isParserAtomIndex()) { 876 const auto* atom = getParserAtom(index.toParserAtomIndex()); 877 return atom->isInstantiatedAsJSAtom(); 878 } 879 880 // Everything else are always JSAtom. 881 return true; 882 } 883 884 uint32_t ParserAtomsTable::length(TaggedParserAtomIndex index) const { 885 if (index.isParserAtomIndex()) { 886 return getParserAtom(index.toParserAtomIndex())->length(); 887 } 888 889 if (index.isWellKnownAtomId()) { 890 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 891 return info.length; 892 } 893 894 if (index.isLength1StaticParserString()) { 895 return 1; 896 } 897 898 if (index.isLength2StaticParserString()) { 899 return 2; 900 } 901 902 MOZ_ASSERT(index.isLength3StaticParserString()); 903 return 3; 904 } 905 906 HashNumber ParserAtomsTable::hash(TaggedParserAtomIndex index) const { 907 if (index.isParserAtomIndex()) { 908 return getParserAtom(index.toParserAtomIndex())->hash(); 909 } 910 911 return index.staticOrWellKnownHash(); 912 } 913 914 double ParserAtomsTable::toNumber(TaggedParserAtomIndex index) const { 915 if (index.isParserAtomIndex()) { 916 const auto* atom = getParserAtom(index.toParserAtomIndex()); 917 size_t len = atom->length(); 918 return atom->hasLatin1Chars() ? CharsToNumber(atom->latin1Chars(), len) 919 : CharsToNumber(atom->twoByteChars(), len); 920 } 921 922 if (index.isWellKnownAtomId()) { 923 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 924 return CharsToNumber(reinterpret_cast<const Latin1Char*>(info.content), 925 info.length); 926 } 927 928 if (index.isLength1StaticParserString()) { 929 Latin1Char content[1]; 930 getLength1Content(index.toLength1StaticParserString(), content); 931 return CharsToNumber(content, 1); 932 } 933 934 if (index.isLength2StaticParserString()) { 935 char content[2]; 936 getLength2Content(index.toLength2StaticParserString(), content); 937 return CharsToNumber(reinterpret_cast<const Latin1Char*>(content), 2); 938 } 939 940 MOZ_ASSERT(index.isLength3StaticParserString()); 941 double result = double(index.toLength3StaticParserString()); 942 #ifdef DEBUG 943 char content[3]; 944 getLength3Content(index.toLength3StaticParserString(), content); 945 double tmp = CharsToNumber(reinterpret_cast<const Latin1Char*>(content), 3); 946 MOZ_ASSERT(tmp == result); 947 #endif 948 return result; 949 } 950 951 UniqueChars ParserAtomsTable::toNewUTF8CharsZ( 952 FrontendContext* fc, TaggedParserAtomIndex index) const { 953 auto* alloc = fc->getAllocator(); 954 955 if (index.isParserAtomIndex()) { 956 const auto* atom = getParserAtom(index.toParserAtomIndex()); 957 return UniqueChars( 958 atom->hasLatin1Chars() 959 ? JS::CharsToNewUTF8CharsZ(alloc, atom->latin1Range()).c_str() 960 : JS::CharsToNewUTF8CharsZ(alloc, atom->twoByteRange()).c_str()); 961 } 962 963 if (index.isWellKnownAtomId()) { 964 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 965 return UniqueChars( 966 JS::CharsToNewUTF8CharsZ( 967 alloc, 968 mozilla::Range(reinterpret_cast<const Latin1Char*>(info.content), 969 info.length)) 970 .c_str()); 971 } 972 973 if (index.isLength1StaticParserString()) { 974 Latin1Char content[1]; 975 getLength1Content(index.toLength1StaticParserString(), content); 976 return UniqueChars( 977 JS::CharsToNewUTF8CharsZ(alloc, mozilla::Range(content, 1)).c_str()); 978 } 979 980 if (index.isLength2StaticParserString()) { 981 char content[2]; 982 getLength2Content(index.toLength2StaticParserString(), content); 983 return UniqueChars( 984 JS::CharsToNewUTF8CharsZ( 985 alloc, 986 mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2)) 987 .c_str()); 988 } 989 990 MOZ_ASSERT(index.isLength3StaticParserString()); 991 char content[3]; 992 getLength3Content(index.toLength3StaticParserString(), content); 993 return UniqueChars( 994 JS::CharsToNewUTF8CharsZ( 995 alloc, 996 mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3)) 997 .c_str()); 998 } 999 1000 template <typename CharT> 1001 UniqueChars ToPrintableStringImpl(mozilla::Range<CharT> str, 1002 char quote = '\0') { 1003 // Pass nullptr as JSContext, given we don't use JSString. 1004 // OOM should be handled by caller. 1005 Sprinter sprinter(nullptr); 1006 if (!sprinter.init()) { 1007 return nullptr; 1008 } 1009 QuoteString<QuoteTarget::String>(&sprinter, str, quote); 1010 return sprinter.release(); 1011 } 1012 1013 UniqueChars ParserAtomsTable::toPrintableString( 1014 TaggedParserAtomIndex index) const { 1015 if (index.isParserAtomIndex()) { 1016 const auto* atom = getParserAtom(index.toParserAtomIndex()); 1017 return atom->hasLatin1Chars() ? ToPrintableStringImpl(atom->latin1Range()) 1018 : ToPrintableStringImpl(atom->twoByteRange()); 1019 } 1020 1021 if (index.isWellKnownAtomId()) { 1022 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 1023 return ToPrintableStringImpl(mozilla::Range( 1024 reinterpret_cast<const Latin1Char*>(info.content), info.length)); 1025 } 1026 1027 if (index.isLength1StaticParserString()) { 1028 Latin1Char content[1]; 1029 getLength1Content(index.toLength1StaticParserString(), content); 1030 return ToPrintableStringImpl(mozilla::Range<const Latin1Char>(content, 1)); 1031 } 1032 1033 if (index.isLength2StaticParserString()) { 1034 char content[2]; 1035 getLength2Content(index.toLength2StaticParserString(), content); 1036 return ToPrintableStringImpl( 1037 mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2)); 1038 } 1039 1040 MOZ_ASSERT(index.isLength3StaticParserString()); 1041 char content[3]; 1042 getLength3Content(index.toLength3StaticParserString(), content); 1043 return ToPrintableStringImpl( 1044 mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3)); 1045 } 1046 1047 UniqueChars ParserAtomsTable::toQuotedString( 1048 TaggedParserAtomIndex index) const { 1049 if (index.isParserAtomIndex()) { 1050 const auto* atom = getParserAtom(index.toParserAtomIndex()); 1051 return atom->hasLatin1Chars() 1052 ? ToPrintableStringImpl(atom->latin1Range(), '\"') 1053 : ToPrintableStringImpl(atom->twoByteRange(), '\"'); 1054 } 1055 1056 if (index.isWellKnownAtomId()) { 1057 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 1058 return ToPrintableStringImpl( 1059 mozilla::Range(reinterpret_cast<const Latin1Char*>(info.content), 1060 info.length), 1061 '\"'); 1062 } 1063 1064 if (index.isLength1StaticParserString()) { 1065 Latin1Char content[1]; 1066 getLength1Content(index.toLength1StaticParserString(), content); 1067 return ToPrintableStringImpl(mozilla::Range<const Latin1Char>(content, 1), 1068 '\"'); 1069 } 1070 1071 if (index.isLength2StaticParserString()) { 1072 char content[2]; 1073 getLength2Content(index.toLength2StaticParserString(), content); 1074 return ToPrintableStringImpl( 1075 mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 2), '\"'); 1076 } 1077 1078 MOZ_ASSERT(index.isLength3StaticParserString()); 1079 char content[3]; 1080 getLength3Content(index.toLength3StaticParserString(), content); 1081 return ToPrintableStringImpl( 1082 mozilla::Range(reinterpret_cast<const Latin1Char*>(content), 3), '\"'); 1083 } 1084 1085 JSAtom* ParserAtomsTable::toJSAtom(JSContext* cx, FrontendContext* fc, 1086 TaggedParserAtomIndex index, 1087 CompilationAtomCache& atomCache) const { 1088 // This function can be called before we instantiate atoms based on 1089 // AtomizeFlag. 1090 1091 if (index.isParserAtomIndex()) { 1092 auto atomIndex = index.toParserAtomIndex(); 1093 1094 // If we already instantiated this parser atom, it should always be JSAtom. 1095 // `asAtom()` called in getAtomAt asserts that. 1096 JSAtom* atom = atomCache.getAtomAt(atomIndex); 1097 if (atom) { 1098 return atom; 1099 } 1100 1101 // For consistency, mark atomize. 1102 ParserAtom* parserAtom = getParserAtom(atomIndex); 1103 parserAtom->markAtomize(ParserAtom::Atomize::Yes); 1104 return parserAtom->instantiateAtom(cx, fc, atomIndex, atomCache); 1105 } 1106 1107 if (index.isWellKnownAtomId()) { 1108 return GetWellKnownAtom(cx, index.toWellKnownAtomId()); 1109 } 1110 1111 if (index.isLength1StaticParserString()) { 1112 char16_t ch = static_cast<char16_t>(index.toLength1StaticParserString()); 1113 return cx->staticStrings().getUnit(ch); 1114 } 1115 1116 if (index.isLength2StaticParserString()) { 1117 size_t s = static_cast<size_t>(index.toLength2StaticParserString()); 1118 return cx->staticStrings().getLength2FromIndex(s); 1119 } 1120 1121 MOZ_ASSERT(index.isLength3StaticParserString()); 1122 uint32_t s = uint32_t(index.toLength3StaticParserString()); 1123 return cx->staticStrings().getUint(s); 1124 } 1125 1126 bool ParserAtomsTable::appendTo(StringBuilder& sb, 1127 TaggedParserAtomIndex index) const { 1128 if (index.isParserAtomIndex()) { 1129 const auto* atom = getParserAtom(index.toParserAtomIndex()); 1130 size_t length = atom->length(); 1131 return atom->hasLatin1Chars() ? sb.append(atom->latin1Chars(), length) 1132 : sb.append(atom->twoByteChars(), length); 1133 } 1134 1135 if (index.isWellKnownAtomId()) { 1136 const auto& info = GetWellKnownAtomInfo(index.toWellKnownAtomId()); 1137 return sb.append(info.content, info.length); 1138 } 1139 1140 if (index.isLength1StaticParserString()) { 1141 Latin1Char content[1]; 1142 getLength1Content(index.toLength1StaticParserString(), content); 1143 return sb.append(content[0]); 1144 } 1145 1146 if (index.isLength2StaticParserString()) { 1147 char content[2]; 1148 getLength2Content(index.toLength2StaticParserString(), content); 1149 return sb.append(content, 2); 1150 } 1151 1152 MOZ_ASSERT(index.isLength3StaticParserString()); 1153 char content[3]; 1154 getLength3Content(index.toLength3StaticParserString(), content); 1155 return sb.append(content, 3); 1156 } 1157 1158 bool InstantiateMarkedAtoms(JSContext* cx, FrontendContext* fc, 1159 const ParserAtomSpan& entries, 1160 CompilationAtomCache& atomCache) { 1161 MOZ_ASSERT(cx->zone()); 1162 1163 for (size_t i = 0; i < entries.size(); i++) { 1164 const auto& entry = entries[i]; 1165 if (!entry) { 1166 continue; 1167 } 1168 if (!entry->isUsedByStencil()) { 1169 continue; 1170 } 1171 1172 auto index = ParserAtomIndex(i); 1173 if (atomCache.hasAtomAt(index)) { 1174 continue; 1175 } 1176 1177 if (!entry->isInstantiatedAsJSAtom()) { 1178 if (!entry->instantiateString(cx, fc, index, atomCache)) { 1179 return false; 1180 } 1181 } else { 1182 if (!entry->instantiateAtom(cx, fc, index, atomCache)) { 1183 return false; 1184 } 1185 } 1186 } 1187 return true; 1188 } 1189 1190 bool InstantiateMarkedAtomsAsPermanent(JSContext* cx, FrontendContext* fc, 1191 AtomSet& atomSet, 1192 const ParserAtomSpan& entries, 1193 CompilationAtomCache& atomCache) { 1194 MOZ_ASSERT(!cx->zone()); 1195 1196 for (size_t i = 0; i < entries.size(); i++) { 1197 const auto& entry = entries[i]; 1198 if (!entry) { 1199 continue; 1200 } 1201 if (!entry->isUsedByStencil()) { 1202 continue; 1203 } 1204 1205 auto index = ParserAtomIndex(i); 1206 if (atomCache.hasAtomAt(index)) { 1207 MOZ_ASSERT(atomCache.getAtomAt(index)->isPermanentAtom()); 1208 continue; 1209 } 1210 1211 if (!entry->instantiatePermanentAtom(cx, fc, atomSet, index, atomCache)) { 1212 return false; 1213 } 1214 } 1215 return true; 1216 } 1217 1218 /* static */ 1219 MOZ_RUNINIT WellKnownParserAtoms WellKnownParserAtoms::singleton_; 1220 1221 template <typename CharT> 1222 TaggedParserAtomIndex WellKnownParserAtoms::lookupChar16Seq( 1223 const SpecificParserAtomLookup<CharT>& lookup) const { 1224 EntryMap::Ptr ptr = wellKnownMap_.readonlyThreadsafeLookup(lookup); 1225 if (ptr) { 1226 return ptr->value(); 1227 } 1228 return TaggedParserAtomIndex::null(); 1229 } 1230 1231 TaggedParserAtomIndex WellKnownParserAtoms::lookupTinyIndexUTF8( 1232 const mozilla::Utf8Unit* utf8Ptr, size_t nbyte) const { 1233 // Check for tiny strings which are abundant in minified code. 1234 if (nbyte == 2 && IsLatin1(utf8Ptr[0], utf8Ptr[1])) { 1235 // Special case the length-1 non-ASCII range. 1236 InflatedChar16Sequence<mozilla::Utf8Unit> seq(utf8Ptr, 2); 1237 char16_t u = seq.next(); 1238 const Latin1Char c = u; 1239 MOZ_ASSERT(!seq.hasMore()); 1240 auto tiny = lookupTinyIndex(&c, 1); 1241 MOZ_ASSERT(tiny); 1242 return tiny; 1243 } 1244 1245 // NOTE: Other than length-1 non-ASCII range, the tiny atoms are all 1246 // ASCII-only so we can directly look at the UTF-8 data without 1247 // worrying about surrogates. 1248 return lookupTinyIndex(reinterpret_cast<const Latin1Char*>(utf8Ptr), nbyte); 1249 } 1250 1251 bool WellKnownParserAtoms::initSingle(const WellKnownAtomInfo& info, 1252 TaggedParserAtomIndex index) { 1253 unsigned int len = info.length; 1254 const Latin1Char* str = reinterpret_cast<const Latin1Char*>(info.content); 1255 1256 // Well-known atoms are all currently ASCII with length <= MaxWellKnownLength. 1257 MOZ_ASSERT(len <= MaxWellKnownLength); 1258 MOZ_ASSERT(mozilla::IsAscii(mozilla::Span(info.content, len))); 1259 1260 // Strings matched by lookupTinyIndex are stored in static table and aliases 1261 // should be initialized directly in WellKnownParserAtoms::init. 1262 MOZ_ASSERT(lookupTinyIndex(str, len) == TaggedParserAtomIndex::null(), 1263 "Well-known atom matches a tiny StaticString. Did you add it to " 1264 "the wrong CommonPropertyNames.h list?"); 1265 1266 InflatedChar16Sequence<Latin1Char> seq(str, len); 1267 SpecificParserAtomLookup<Latin1Char> lookup(seq, info.hash); 1268 1269 // Save name for returning after moving entry into set. 1270 if (!wellKnownMap_.putNew(lookup, &info, index)) { 1271 return false; 1272 } 1273 1274 return true; 1275 } 1276 1277 bool WellKnownParserAtoms::init() { 1278 MOZ_ASSERT(wellKnownMap_.empty()); 1279 1280 // Add well-known strings to the HashMap. The HashMap is used for dynamic 1281 // lookups later and does not change once this init method is complete. 1282 #define COMMON_NAME_INIT_(NAME, _) \ 1283 if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \ 1284 TaggedParserAtomIndex::WellKnown::NAME())) { \ 1285 return false; \ 1286 } 1287 FOR_EACH_NONTINY_COMMON_PROPERTYNAME(COMMON_NAME_INIT_) 1288 #undef COMMON_NAME_INIT_ 1289 #define COMMON_NAME_INIT_(NAME, _) \ 1290 if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \ 1291 TaggedParserAtomIndex::WellKnown::NAME())) { \ 1292 return false; \ 1293 } 1294 JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INIT_) 1295 #undef COMMON_NAME_INIT_ 1296 #define COMMON_NAME_INIT_(NAME) \ 1297 if (!initSingle(GetWellKnownAtomInfo(WellKnownAtomId::NAME), \ 1298 TaggedParserAtomIndex::WellKnown::NAME())) { \ 1299 return false; \ 1300 } 1301 JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INIT_) 1302 #undef COMMON_NAME_INIT_ 1303 1304 return true; 1305 } 1306 1307 void WellKnownParserAtoms::free() { wellKnownMap_.clear(); } 1308 1309 /* static */ bool WellKnownParserAtoms::initSingleton() { 1310 return singleton_.init(); 1311 } 1312 1313 /* static */ void WellKnownParserAtoms::freeSingleton() { singleton_.free(); } 1314 1315 } /* namespace frontend */ 1316 } /* namespace js */