LCOV - code coverage report
Current view: top level - js/src/vm - JSAtom.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 44 324 13.6 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /*
       8             :  * JS atom table.
       9             :  */
      10             : 
      11             : #include "vm/JSAtom-inl.h"
      12             : 
      13             : #include "mozilla/ArrayUtils.h"
      14             : #include "mozilla/EndianUtils.h"
      15             : #include "mozilla/RangedPtr.h"
      16             : #include "mozilla/Unused.h"
      17             : 
      18             : #include <string.h>
      19             : 
      20             : #include "jstypes.h"
      21             : 
      22             : #include "builtin/String.h"
      23             : #include "gc/Marking.h"
      24             : #include "util/Text.h"
      25             : #include "vm/JSContext.h"
      26             : #include "vm/SymbolType.h"
      27             : #include "vm/Xdr.h"
      28             : 
      29             : #include "gc/AtomMarking-inl.h"
      30             : #include "vm/JSContext-inl.h"
      31             : #include "vm/JSObject-inl.h"
      32             : #include "vm/Realm-inl.h"
      33             : #include "vm/StringType-inl.h"
      34             : 
      35             : using namespace js;
      36             : using namespace js::gc;
      37             : 
      38             : using mozilla::ArrayEnd;
      39             : using mozilla::ArrayLength;
      40             : using mozilla::Maybe;
      41             : using mozilla::Nothing;
      42             : using mozilla::RangedPtr;
      43             : 
      44           0 : struct js::AtomHasher::Lookup
      45             : {
      46             :     union {
      47             :         const JS::Latin1Char* latin1Chars;
      48             :         const char16_t* twoByteChars;
      49             :     };
      50             :     bool isLatin1;
      51             :     size_t length;
      52             :     const JSAtom* atom; /* Optional. */
      53             :     JS::AutoCheckCannotGC nogc;
      54             : 
      55             :     HashNumber hash;
      56             : 
      57           0 :     MOZ_ALWAYS_INLINE Lookup(const char16_t* chars, size_t length)
      58           0 :       : twoByteChars(chars), isLatin1(false), length(length), atom(nullptr),
      59           0 :         hash(mozilla::HashString(chars, length))
      60           0 :     {}
      61             : 
      62           0 :     MOZ_ALWAYS_INLINE Lookup(const JS::Latin1Char* chars, size_t length)
      63           0 :       : latin1Chars(chars), isLatin1(true), length(length), atom(nullptr),
      64           0 :         hash(mozilla::HashString(chars, length))
      65           0 :     {}
      66             : 
      67           0 :     inline explicit Lookup(const JSAtom* atom)
      68           0 :       : isLatin1(atom->hasLatin1Chars()), length(atom->length()), atom(atom),
      69           0 :         hash(atom->hash())
      70             :     {
      71           0 :         if (isLatin1) {
      72           0 :             latin1Chars = atom->latin1Chars(nogc);
      73           0 :             MOZ_ASSERT(mozilla::HashString(latin1Chars, length) == hash);
      74             :         } else {
      75           0 :             twoByteChars = atom->twoByteChars(nogc);
      76           0 :             MOZ_ASSERT(mozilla::HashString(twoByteChars, length) == hash);
      77             :         }
      78           0 :     }
      79             : };
      80             : 
      81             : inline HashNumber
      82             : js::AtomHasher::hash(const Lookup& l)
      83             : {
      84             :     return l.hash;
      85             : }
      86             : 
      87             : MOZ_ALWAYS_INLINE bool
      88           0 : js::AtomHasher::match(const AtomStateEntry& entry, const Lookup& lookup)
      89             : {
      90           0 :     JSAtom* key = entry.asPtrUnbarriered();
      91           0 :     if (lookup.atom)
      92           0 :         return lookup.atom == key;
      93           0 :     if (key->length() != lookup.length || key->hash() != lookup.hash)
      94             :         return false;
      95             : 
      96           0 :     if (key->hasLatin1Chars()) {
      97           0 :         const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
      98           0 :         if (lookup.isLatin1)
      99           0 :             return mozilla::PodEqual(keyChars, lookup.latin1Chars, lookup.length);
     100           0 :         return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
     101             :     }
     102             : 
     103           0 :     const char16_t* keyChars = key->twoByteChars(lookup.nogc);
     104           0 :     if (lookup.isLatin1)
     105           0 :         return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
     106           0 :     return mozilla::PodEqual(keyChars, lookup.twoByteChars, lookup.length);
     107             : }
     108             : 
     109             : inline JSAtom*
     110           0 : js::AtomStateEntry::asPtr(JSContext* cx) const
     111             : {
     112           0 :     JSAtom* atom = asPtrUnbarriered();
     113           0 :     if (!cx->helperThread())
     114           0 :         JSString::readBarrier(atom);
     115           0 :     return atom;
     116             : }
     117             : 
     118             : const char*
     119           0 : js::AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes)
     120             : {
     121           0 :     JSString* str = QuoteString(cx, atom, 0);
     122           0 :     if (!str)
     123             :         return nullptr;
     124           0 :     return bytes->encodeLatin1(cx, str);
     125             : }
     126             : 
     127             : #define DEFINE_PROTO_STRING(name,init,clasp) const char js_##name##_str[] = #name;
     128             : JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
     129             : #undef DEFINE_PROTO_STRING
     130             : 
     131             : #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
     132             : FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
     133             : #undef CONST_CHAR_STR
     134             : 
     135             : // Use a low initial capacity for atom hash tables to avoid penalizing runtimes
     136             : // which create a small number of atoms.
     137             : static const uint32_t JS_STRING_HASH_COUNT = 64;
     138             : 
     139             : MOZ_ALWAYS_INLINE AtomSet::Ptr
     140             : js::FrozenAtomSet::readonlyThreadsafeLookup(const AtomSet::Lookup& l) const
     141             : {
     142           0 :     return mSet->readonlyThreadsafeLookup(l);
     143             : }
     144             : 
     145             : struct CommonNameInfo
     146             : {
     147             :     const char* str;
     148             :     size_t length;
     149             : };
     150             : 
     151             : bool
     152           0 : JSRuntime::initializeAtoms(JSContext* cx)
     153             : {
     154           0 :     atoms_ = js_new<AtomSet>();
     155           0 :     if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
     156             :         return false;
     157             : 
     158             :     // |permanentAtoms| hasn't been created yet.
     159           0 :     MOZ_ASSERT(!permanentAtoms);
     160             : 
     161           0 :     if (parentRuntime) {
     162           0 :         staticStrings = parentRuntime->staticStrings;
     163           0 :         commonNames = parentRuntime->commonNames;
     164           0 :         emptyString = parentRuntime->emptyString;
     165           0 :         permanentAtoms = parentRuntime->permanentAtoms;
     166           0 :         wellKnownSymbols = parentRuntime->wellKnownSymbols;
     167           0 :         return true;
     168             :     }
     169             : 
     170           0 :     staticStrings = js_new<StaticStrings>();
     171           0 :     if (!staticStrings || !staticStrings->init(cx))
     172             :         return false;
     173             : 
     174             :     static const CommonNameInfo cachedNames[] = {
     175             : #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
     176             :         FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
     177             : #undef COMMON_NAME_INFO
     178             : #define COMMON_NAME_INFO(name, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
     179             :         JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
     180             : #undef COMMON_NAME_INFO
     181             : #define COMMON_NAME_INFO(name) { #name, sizeof(#name) - 1 },
     182             :         JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
     183             : #undef COMMON_NAME_INFO
     184             : #define COMMON_NAME_INFO(name) { "Symbol." #name, sizeof("Symbol." #name) - 1 },
     185             :         JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
     186             : #undef COMMON_NAME_INFO
     187             :     };
     188             : 
     189           0 :     commonNames = js_new<JSAtomState>();
     190           0 :     if (!commonNames)
     191             :         return false;
     192             : 
     193             :     ImmutablePropertyNamePtr* names = reinterpret_cast<ImmutablePropertyNamePtr*>(commonNames.ref());
     194           0 :     for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
     195           0 :         JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, PinAtom);
     196           0 :         if (!atom)
     197             :             return false;
     198           0 :         names->init(atom->asPropertyName());
     199             :     }
     200           0 :     MOZ_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
     201             : 
     202           0 :     emptyString = commonNames->empty;
     203             : 
     204             :     // Create the well-known symbols.
     205           0 :     wellKnownSymbols = js_new<WellKnownSymbols>();
     206           0 :     if (!wellKnownSymbols)
     207             :         return false;
     208             : 
     209           0 :     ImmutablePropertyNamePtr* descriptions = commonNames->wellKnownSymbolDescriptions();
     210           0 :     ImmutableSymbolPtr* symbols = reinterpret_cast<ImmutableSymbolPtr*>(wellKnownSymbols.ref());
     211           0 :     for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
     212           0 :         JS::Symbol* symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]);
     213           0 :         if (!symbol) {
     214           0 :             ReportOutOfMemory(cx);
     215           0 :             return false;
     216             :         }
     217           0 :         symbols[i].init(symbol);
     218             :     }
     219             : 
     220             :     return true;
     221             : }
     222             : 
     223             : void
     224           0 : JSRuntime::finishAtoms()
     225             : {
     226           0 :     js_delete(atoms_.ref());
     227             : 
     228           0 :     if (!parentRuntime) {
     229           0 :         js_delete(staticStrings.ref());
     230           0 :         js_delete(commonNames.ref());
     231           0 :         js_delete(permanentAtoms.ref());
     232           0 :         js_delete(wellKnownSymbols.ref());
     233             :     }
     234             : 
     235           0 :     atoms_ = nullptr;
     236           0 :     staticStrings = nullptr;
     237           0 :     commonNames = nullptr;
     238           0 :     permanentAtoms = nullptr;
     239           0 :     wellKnownSymbols = nullptr;
     240           0 :     emptyString = nullptr;
     241           0 : }
     242             : 
     243             : static inline void
     244           0 : TracePinnedAtoms(JSTracer* trc, const AtomSet& atoms)
     245             : {
     246           0 :     for (auto r = atoms.all(); !r.empty(); r.popFront()) {
     247           0 :         const AtomStateEntry& entry = r.front();
     248           0 :         MOZ_ASSERT(entry.isPinned() == entry.asPtrUnbarriered()->isPinned());
     249           0 :         if (entry.isPinned()) {
     250           0 :             JSAtom* atom = entry.asPtrUnbarriered();
     251           0 :             TraceRoot(trc, &atom, "interned_atom");
     252           0 :             MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
     253             :         }
     254             :     }
     255           0 : }
     256             : 
     257             : void
     258           0 : js::TraceAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
     259             : {
     260           0 :     JSRuntime* rt = trc->runtime();
     261             : 
     262           0 :     if (rt->atomsAreFinished())
     263             :         return;
     264             : 
     265           0 :     TracePinnedAtoms(trc, rt->atoms(lock));
     266           0 :     if (rt->atomsAddedWhileSweeping())
     267           0 :         TracePinnedAtoms(trc, *rt->atomsAddedWhileSweeping());
     268             : }
     269             : 
     270             : void
     271           0 : js::TracePermanentAtoms(JSTracer* trc)
     272             : {
     273           0 :     JSRuntime* rt = trc->runtime();
     274             : 
     275             :     // Permanent atoms only need to be traced in the runtime which owns them.
     276           0 :     if (rt->parentRuntime)
     277             :         return;
     278             : 
     279             :     // Static strings are not included in the permanent atoms table.
     280           0 :     if (rt->staticStrings)
     281           0 :         rt->staticStrings->trace(trc);
     282             : 
     283           0 :     if (rt->permanentAtoms) {
     284           0 :         for (FrozenAtomSet::Range r(rt->permanentAtoms->all()); !r.empty(); r.popFront()) {
     285           0 :             const AtomStateEntry& entry = r.front();
     286             : 
     287           0 :             JSAtom* atom = entry.asPtrUnbarriered();
     288           0 :             MOZ_ASSERT(atom->isPinned());
     289           0 :             TraceProcessGlobalRoot(trc, atom, "permanent atom");
     290             :         }
     291             :     }
     292             : }
     293             : 
     294             : void
     295           0 : js::TraceWellKnownSymbols(JSTracer* trc)
     296             : {
     297           0 :     JSRuntime* rt = trc->runtime();
     298             : 
     299           0 :     if (rt->parentRuntime)
     300             :         return;
     301             : 
     302           0 :     if (WellKnownSymbols* wks = rt->wellKnownSymbols) {
     303           0 :         for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++)
     304           0 :             TraceProcessGlobalRoot(trc, wks->get(i).get(), "well_known_symbol");
     305             :     }
     306             : }
     307             : 
     308             : bool
     309           0 : JSRuntime::transformToPermanentAtoms(JSContext* cx)
     310             : {
     311           0 :     MOZ_ASSERT(!parentRuntime);
     312             : 
     313             :     // All static strings were created as permanent atoms, now move the contents
     314             :     // of the atoms table into permanentAtoms and mark each as permanent.
     315             : 
     316           0 :     MOZ_ASSERT(!permanentAtoms);
     317           0 :     permanentAtoms = js_new<FrozenAtomSet>(atoms_);   // takes ownership of atoms_
     318             : 
     319           0 :     atoms_ = js_new<AtomSet>();
     320           0 :     if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
     321             :         return false;
     322             : 
     323           0 :     for (FrozenAtomSet::Range r(permanentAtoms->all()); !r.empty(); r.popFront()) {
     324           0 :         AtomStateEntry entry = r.front();
     325           0 :         JSAtom* atom = entry.asPtr(cx);
     326           0 :         atom->morphIntoPermanentAtom();
     327             :     }
     328             : 
     329           0 :     return true;
     330             : }
     331             : 
     332             : static inline AtomSet::Ptr
     333           0 : LookupAtomState(JSRuntime* rt, const AtomHasher::Lookup& lookup)
     334             : {
     335           0 :     MOZ_ASSERT(rt->currentThreadHasExclusiveAccess());
     336             : 
     337           0 :     AtomSet::Ptr p = rt->unsafeAtoms().lookup(lookup); // Safe because we hold the lock.
     338           0 :     if (!p && rt->atomsAddedWhileSweeping())
     339           0 :         p = rt->atomsAddedWhileSweeping()->lookup(lookup);
     340           0 :     return p;
     341             : }
     342             : 
     343             : template <typename CharT>
     344             : MOZ_ALWAYS_INLINE
     345             : static JSAtom*
     346             : AtomizeAndCopyCharsInner(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
     347             :                          const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup);
     348             : 
     349             : /* |tbchars| must not point into an inline or short string. */
     350             : template <typename CharT>
     351             : MOZ_ALWAYS_INLINE
     352             : static JSAtom*
     353           0 : AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
     354             :                     const Maybe<uint32_t>& indexValue)
     355             : {
     356           0 :     if (JSAtom* s = cx->staticStrings().lookup(tbchars, length))
     357             :         return s;
     358             : 
     359           0 :     AtomHasher::Lookup lookup(tbchars, length);
     360             : 
     361             :     // Try the per-Zone cache first. If we find the atom there we can avoid the
     362             :     // atoms lock, the markAtom call, and the multiple HashSet lookups below.
     363             :     // We don't use the per-Zone cache if we want a pinned atom: handling that
     364             :     // is more complicated and pinning atoms is relatively uncommon.
     365           0 :     Zone* zone = cx->zone();
     366           0 :     Maybe<AtomSet::AddPtr> zonePtr;
     367           0 :     if (MOZ_LIKELY(zone && pin == DoNotPinAtom)) {
     368           0 :         zonePtr.emplace(zone->atomCache().lookupForAdd(lookup));
     369           0 :         if (zonePtr.ref()) {
     370             :             // The cache is purged on GC so if we're in the middle of an
     371             :             // incremental GC we should have barriered the atom when we put
     372             :             // it in the cache.
     373           0 :             JSAtom* atom = zonePtr.ref()->asPtrUnbarriered();
     374           0 :             MOZ_ASSERT(AtomIsMarked(zone, atom));
     375             :             return atom;
     376             :         }
     377             :     }
     378             : 
     379             :     // Note: when this function is called while the permanent atoms table is
     380             :     // being initialized (in initializeAtoms()), |permanentAtoms| is not yet
     381             :     // initialized so this lookup is always skipped. Only once
     382             :     // transformToPermanentAtoms() is called does |permanentAtoms| get
     383             :     // initialized and then this lookup will go ahead.
     384           0 :     if (cx->isPermanentAtomsInitialized()) {
     385           0 :         AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
     386           0 :         if (pp) {
     387           0 :             JSAtom* atom = pp->asPtr(cx);
     388           0 :             if (zonePtr && !zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false))) {
     389           0 :                 ReportOutOfMemory(cx);
     390           0 :                 return nullptr;
     391             :             }
     392             : 
     393             :             return atom;
     394             :         }
     395             :     }
     396             : 
     397             :     // Validate the length before taking the exclusive access lock, as throwing
     398             :     // an exception here may reenter this code.
     399           0 :     if (MOZ_UNLIKELY(!JSString::validateLength(cx, length)))
     400             :         return nullptr;
     401             : 
     402       66631 :     JSAtom* atom = AtomizeAndCopyCharsInner(cx, tbchars, length, pin, indexValue, lookup);
     403           0 :     if (!atom)
     404             :         return nullptr;
     405             : 
     406           0 :     cx->atomMarking().inlinedMarkAtom(cx, atom);
     407             : 
     408      250206 :     if (zonePtr && !zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false))) {
     409           0 :         ReportOutOfMemory(cx);
     410           0 :         return nullptr;
     411             :     }
     412             : 
     413             :     return atom;
     414             : }
     415             : 
     416             : template <typename CharT>
     417             : MOZ_ALWAYS_INLINE
     418             : static JSAtom*
     419           0 : AtomizeAndCopyCharsInner(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
     420             :                          const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup)
     421             : {
     422           0 :     AutoLockForExclusiveAccess lock(cx);
     423             : 
     424           0 :     JSRuntime* rt = cx->runtime();
     425           0 :     AtomSet& atoms = rt->atoms(lock);
     426       66631 :     AtomSet* atomsAddedWhileSweeping = rt->atomsAddedWhileSweeping();
     427       66631 :     AtomSet::AddPtr p;
     428             : 
     429           0 :     if (!atomsAddedWhileSweeping) {
     430           0 :         p = atoms.lookupForAdd(lookup);
     431             :     } else {
     432             :         // We're currently sweeping the main atoms table and all new atoms will
     433             :         // be added to a secondary table. Check this first.
     434           0 :         MOZ_ASSERT(rt->atomsZone(lock)->isGCSweeping());
     435           0 :         p = atomsAddedWhileSweeping->lookupForAdd(lookup);
     436             : 
     437             :         // If that fails check the main table but check if any atom found there
     438             :         // is dead.
     439           0 :         if (!p) {
     440           0 :             if (AtomSet::AddPtr p2 = atoms.lookupForAdd(lookup)) {
     441           0 :                 JSAtom* atom = p2->asPtrUnbarriered();
     442           0 :                 if (!IsAboutToBeFinalizedUnbarriered(&atom))
     443           0 :                     p = p2;
     444             :             }
     445             :         }
     446             :     }
     447             : 
     448       66631 :     if (p) {
     449       18134 :         JSAtom* atom = p->asPtr(cx);
     450       21946 :         if (pin && !atom->isPinned()) {
     451        1146 :             atom->setPinned();
     452        1146 :             p->setPinned(true);
     453             :         }
     454             :         return atom;
     455             :     }
     456             : 
     457             :     JSAtom* atom;
     458             :     {
     459       96994 :         AutoAtomsZone az(cx, lock);
     460             : 
     461           0 :         JSFlatString* flat = NewStringCopyN<NoGC>(cx, tbchars, length);
     462           0 :         if (!flat) {
     463             :             // Grudgingly forgo last-ditch GC. The alternative would be to release
     464             :             // the lock, manually GC here, and retry from the top. If you fix this,
     465             :             // please also fix or comment the similar case in Symbol::new_.
     466           0 :             ReportOutOfMemory(cx);
     467           0 :             return nullptr;
     468             :         }
     469             : 
     470       48497 :         atom = flat->morphAtomizedStringIntoAtom(lookup.hash);
     471           0 :         MOZ_ASSERT(atom->hash() == lookup.hash);
     472             : 
     473       48497 :         if (pin)
     474        4347 :             atom->setPinned();
     475             : 
     476       48497 :         if (indexValue)
     477           0 :             atom->maybeInitializeIndex(*indexValue, true);
     478             : 
     479             :         // We have held the lock since looking up p, and the operations we've done
     480             :         // since then can't GC; therefore the atoms table has not been modified and
     481             :         // p is still valid.
     482       48497 :         AtomSet* addSet = atomsAddedWhileSweeping ? atomsAddedWhileSweeping : &atoms;
     483       96994 :         if (!addSet->add(p, AtomStateEntry(atom, bool(pin)))) {
     484           0 :             ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
     485           0 :             return nullptr;
     486             :         }
     487             :     }
     488             : 
     489       48497 :     return atom;
     490             : }
     491             : 
     492             : template JSAtom*
     493             : AtomizeAndCopyChars(JSContext* cx, const char16_t* tbchars, size_t length, PinningBehavior pin,
     494             :                     const Maybe<uint32_t>& indexValue);
     495             : 
     496             : template JSAtom*
     497             : AtomizeAndCopyChars(JSContext* cx, const Latin1Char* tbchars, size_t length, PinningBehavior pin,
     498             :                     const Maybe<uint32_t>& indexValue);
     499             : 
     500             : JSAtom*
     501       34685 : js::AtomizeString(JSContext* cx, JSString* str,
     502             :                   js::PinningBehavior pin /* = js::DoNotPinAtom */)
     503             : {
     504       69370 :     if (str->isAtom()) {
     505           0 :         JSAtom& atom = str->asAtom();
     506             :         /* N.B. static atoms are effectively always interned. */
     507           0 :         if (pin != PinAtom || atom.isPinned())
     508             :             return &atom;
     509             : 
     510           0 :         AtomHasher::Lookup lookup(&atom);
     511             : 
     512           0 :         AutoLockForExclusiveAccess lock(cx);
     513             : 
     514           0 :         AtomSet::Ptr p = LookupAtomState(cx->runtime(), lookup);
     515           0 :         MOZ_ASSERT(p); // Unpinned atoms must exist in atoms table.
     516           0 :         MOZ_ASSERT(p->asPtrUnbarriered() == &atom);
     517             : 
     518           0 :         MOZ_ASSERT(pin == PinAtom);
     519           0 :         atom.setPinned();
     520           0 :         p->setPinned(true);
     521             : 
     522             :         return &atom;
     523             :     }
     524             : 
     525           0 :     JSLinearString* linear = str->ensureLinear(cx);
     526           0 :     if (!linear)
     527             :         return nullptr;
     528             : 
     529           0 :     Maybe<uint32_t> indexValue;
     530           0 :     if (str->hasIndexValue())
     531           0 :         indexValue.emplace(str->getIndexValue());
     532             : 
     533       32614 :     JS::AutoCheckCannotGC nogc;
     534       16307 :     return linear->hasLatin1Chars()
     535           0 :            ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin, indexValue)
     536       18263 :            : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin, indexValue);
     537             : }
     538             : 
     539             : JSAtom*
     540           0 : js::Atomize(JSContext* cx, const char* bytes, size_t length, PinningBehavior pin,
     541             :             const Maybe<uint32_t>& indexValue)
     542             : {
     543       90233 :     CHECK_REQUEST(cx);
     544             : 
     545       45119 :     const Latin1Char* chars = reinterpret_cast<const Latin1Char*>(bytes);
     546           0 :     return AtomizeAndCopyChars(cx, chars, length, pin, indexValue);
     547             : }
     548             : 
     549             : template <typename CharT>
     550             : JSAtom*
     551      309416 : js::AtomizeChars(JSContext* cx, const CharT* chars, size_t length, PinningBehavior pin)
     552             : {
     553      618850 :     CHECK_REQUEST(cx);
     554      928284 :     return AtomizeAndCopyChars(cx, chars, length, pin, Nothing());
     555             : }
     556             : 
     557             : template JSAtom*
     558             : js::AtomizeChars(JSContext* cx, const Latin1Char* chars, size_t length, PinningBehavior pin);
     559             : 
     560             : template JSAtom*
     561             : js::AtomizeChars(JSContext* cx, const char16_t* chars, size_t length, PinningBehavior pin);
     562             : 
     563             : JSAtom*
     564        1351 : js::AtomizeUTF8Chars(JSContext* cx, const char* utf8Chars, size_t utf8ByteLength)
     565             : {
     566             :     // This could be optimized to hand the char16_t's directly to the JSAtom
     567             :     // instead of making a copy. UTF8CharsToNewTwoByteCharsZ should be
     568             :     // refactored to take an JSContext so that this function could also.
     569             : 
     570        1351 :     UTF8Chars utf8(utf8Chars, utf8ByteLength);
     571             : 
     572             :     size_t length;
     573        4053 :     UniqueTwoByteChars chars(JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get());
     574        1351 :     if (!chars)
     575             :         return nullptr;
     576             : 
     577        2702 :     return AtomizeChars(cx, chars.get(), length);
     578             : }
     579             : 
     580             : bool
     581           0 : js::IndexToIdSlow(JSContext* cx, uint32_t index, MutableHandleId idp)
     582             : {
     583           0 :     MOZ_ASSERT(index > JSID_INT_MAX);
     584             : 
     585             :     char16_t buf[UINT32_CHAR_BUFFER_LENGTH];
     586           0 :     RangedPtr<char16_t> end(ArrayEnd(buf), buf, ArrayEnd(buf));
     587           0 :     RangedPtr<char16_t> start = BackfillIndexInCharBuffer(index, end);
     588             : 
     589           0 :     JSAtom* atom = AtomizeChars(cx, start.get(), end - start);
     590           0 :     if (!atom)
     591             :         return false;
     592             : 
     593           0 :     idp.set(JSID_FROM_BITS((size_t)atom | JSID_TYPE_STRING));
     594           0 :     return true;
     595             : }
     596             : 
     597             : template <AllowGC allowGC>
     598             : static JSAtom*
     599           0 : ToAtomSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
     600             : {
     601           0 :     MOZ_ASSERT(!arg.isString());
     602             : 
     603           0 :     Value v = arg;
     604           0 :     if (!v.isPrimitive()) {
     605           0 :         MOZ_ASSERT(!cx->helperThread());
     606             :         if (!allowGC)
     607           0 :             return nullptr;
     608           0 :         RootedValue v2(cx, v);
     609           0 :         if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
     610           0 :             return nullptr;
     611           0 :         v = v2;
     612             :     }
     613             : 
     614           0 :     if (v.isString()) {
     615           0 :         JSAtom* atom = AtomizeString(cx, v.toString());
     616           0 :         if (!allowGC && !atom)
     617           0 :             cx->recoverFromOutOfMemory();
     618           0 :         return atom;
     619             :     }
     620           0 :     if (v.isInt32()) {
     621           0 :         JSAtom* atom = Int32ToAtom(cx, v.toInt32());
     622           0 :         if (!allowGC && !atom)
     623           0 :             cx->recoverFromOutOfMemory();
     624           0 :         return atom;
     625             :     }
     626           0 :     if (v.isDouble()) {
     627           0 :         JSAtom* atom = NumberToAtom(cx, v.toDouble());
     628           0 :         if (!allowGC && !atom)
     629           0 :             cx->recoverFromOutOfMemory();
     630           0 :         return atom;
     631             :     }
     632           0 :     if (v.isBoolean())
     633           0 :         return v.toBoolean() ? cx->names().true_ : cx->names().false_;
     634           0 :     if (v.isNull())
     635           0 :         return cx->names().null;
     636           0 :     if (v.isSymbol()) {
     637           0 :         MOZ_ASSERT(!cx->helperThread());
     638             :         if (allowGC) {
     639           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     640             :                                       JSMSG_SYMBOL_TO_STRING);
     641             :         }
     642           0 :         return nullptr;
     643             :     }
     644             : #ifdef ENABLE_BIGINT
     645             :     if (v.isBigInt()) {
     646             :         JSAtom* atom = BigIntToAtom(cx, v.toBigInt());
     647             :         if (!allowGC && !atom)
     648             :             cx->recoverFromOutOfMemory();
     649             :         return atom;
     650             :     }
     651             : #endif
     652           0 :     MOZ_ASSERT(v.isUndefined());
     653           0 :     return cx->names().undefined;
     654             : }
     655             : 
     656             : template <AllowGC allowGC>
     657             : JSAtom*
     658           0 : js::ToAtom(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v)
     659             : {
     660           0 :     if (!v.isString())
     661           0 :         return ToAtomSlow<allowGC>(cx, v);
     662             : 
     663           0 :     JSString* str = v.toString();
     664           1 :     if (str->isAtom())
     665           1 :         return &str->asAtom();
     666             : 
     667           0 :     JSAtom* atom = AtomizeString(cx, str);
     668           0 :     if (!atom && !allowGC) {
     669           0 :         MOZ_ASSERT_IF(!cx->helperThread(), cx->isThrowingOutOfMemory());
     670           0 :         cx->recoverFromOutOfMemory();
     671             :     }
     672       11793 :     return atom;
     673             : }
     674             : 
     675             : template JSAtom*
     676             : js::ToAtom<CanGC>(JSContext* cx, HandleValue v);
     677             : 
     678             : template JSAtom*
     679             : js::ToAtom<NoGC>(JSContext* cx, const Value& v);
     680             : 
     681             : template<XDRMode mode>
     682             : XDRResult
     683      306116 : js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
     684             : {
     685           0 :     bool latin1 = false;
     686           0 :     uint32_t length = 0;
     687           0 :     uint32_t lengthAndEncoding = 0;
     688             :     if (mode == XDR_ENCODE) {
     689             :         static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
     690           0 :         latin1 = atomp->hasLatin1Chars();
     691      612232 :         length = atomp->length();
     692      306116 :         lengthAndEncoding = (length << 1) | uint32_t(latin1);
     693             :     }
     694             : 
     695      918348 :     MOZ_TRY(xdr->codeUint32(&lengthAndEncoding));
     696             : 
     697             :     if (mode == XDR_DECODE) {
     698           0 :         length = lengthAndEncoding >> 1;
     699           0 :         latin1 = lengthAndEncoding & 0x1;
     700             :     }
     701             : 
     702             :     // We need to align the string in the XDR buffer such that we can avoid
     703             :     // non-align loads of 16bits characters.
     704           0 :     if (!latin1)
     705           0 :         MOZ_TRY(xdr->codeAlign(sizeof(char16_t)));
     706             : 
     707             :     if (mode == XDR_ENCODE) {
     708      612232 :         JS::AutoCheckCannotGC nogc;
     709      306116 :         if (latin1)
     710      918294 :             return xdr->codeChars(atomp->latin1Chars(nogc), length);
     711           0 :         return xdr->codeChars(const_cast<char16_t*>(atomp->twoByteChars(nogc)), length);
     712             :     }
     713             : 
     714             :     MOZ_ASSERT(mode == XDR_DECODE);
     715             :     /* Avoid JSString allocation for already existing atoms. See bug 321985. */
     716           0 :     JSContext* cx = xdr->cx();
     717             :     JSAtom* atom;
     718           0 :     if (latin1) {
     719           0 :         const Latin1Char* chars = nullptr;
     720           0 :         if (length) {
     721             :             const uint8_t *ptr;
     722           0 :             size_t nbyte = length * sizeof(Latin1Char);
     723           0 :             MOZ_TRY(xdr->peekData(&ptr, nbyte));
     724           0 :             chars = reinterpret_cast<const Latin1Char*>(ptr);
     725             :         }
     726           0 :         atom = AtomizeChars(cx, chars, length);
     727             :     } else {
     728             : #if MOZ_LITTLE_ENDIAN
     729             :         /* Directly access the little endian chars in the XDR buffer. */
     730           0 :         const char16_t* chars = nullptr;
     731           0 :         if (length) {
     732             :             const uint8_t *ptr;
     733           0 :             size_t nbyte = length * sizeof(char16_t);
     734           0 :             MOZ_TRY(xdr->peekData(&ptr, nbyte));
     735           0 :             MOZ_ASSERT(reinterpret_cast<uintptr_t>(ptr) % sizeof(char16_t) == 0,
     736             :                        "non-aligned buffer during JSAtom decoding");
     737           0 :             chars = reinterpret_cast<const char16_t*>(ptr);
     738             :         }
     739           0 :         atom = AtomizeChars(cx, chars, length);
     740             : #else
     741             :         /*
     742             :          * We must copy chars to a temporary buffer to convert between little and
     743             :          * big endian data.
     744             :          */
     745             :         char16_t* chars;
     746             :         char16_t stackChars[256];
     747             :         if (length <= ArrayLength(stackChars)) {
     748             :             chars = stackChars;
     749             :         } else {
     750             :             /*
     751             :              * This is very uncommon. Don't use the tempLifoAlloc arena for this as
     752             :              * most allocations here will be bigger than tempLifoAlloc's default
     753             :              * chunk size.
     754             :              */
     755             :             chars = cx->pod_malloc<char16_t>(length);
     756             :             if (!chars)
     757             :                 return xdr->fail(JS::TranscodeResult_Throw);
     758             :         }
     759             : 
     760             :         MOZ_TRY(xdr->codeChars(chars, length));
     761             :         atom = AtomizeChars(cx, chars, length);
     762             :         if (chars != stackChars)
     763             :             js_free(chars);
     764             : #endif /* !MOZ_LITTLE_ENDIAN */
     765             :     }
     766             : 
     767           0 :     if (!atom)
     768           0 :         return xdr->fail(JS::TranscodeResult_Throw);
     769           0 :     atomp.set(atom);
     770           0 :     return Ok();
     771             : }
     772             : 
     773             : template XDRResult
     774             : js::XDRAtom(XDRState<XDR_ENCODE>* xdr, MutableHandleAtom atomp);
     775             : 
     776             : template XDRResult
     777             : js::XDRAtom(XDRState<XDR_DECODE>* xdr, MutableHandleAtom atomp);
     778             : 
     779             : Handle<PropertyName*>
     780         603 : js::ClassName(JSProtoKey key, JSContext* cx)
     781             : {
     782         603 :     return ClassName(key, cx->names());
     783             : }
     784             : 
     785             : void
     786           0 : js::gc::MergeAtomsAddedWhileSweeping(JSRuntime* rt)
     787             : {
     788             :     // Add atoms that were added to the secondary table while we were sweeping
     789             :     // the main table.
     790             : 
     791           0 :     AutoEnterOOMUnsafeRegion oomUnsafe;
     792           0 :     AtomSet* atomsTable = rt->atomsForSweeping();
     793           0 :     MOZ_ASSERT(atomsTable);
     794             :     for (auto r = rt->atomsAddedWhileSweeping()->all(); !r.empty(); r.popFront()) {
     795             :         if (!atomsTable->putNew(AtomHasher::Lookup(r.front().asPtrUnbarriered()), r.front()))
     796             :             oomUnsafe.crash("Adding atom from secondary table after sweep");
     797             :     }
     798             : }

Generated by: LCOV version 1.13-14-ga5dd952