LCOV - code coverage report
Current view: top level - js/src - jsatom.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 224 312 71.8 %
Date: 2017-07-14 16:53:18 Functions: 21 26 80.8 %
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 "jsatominlines.h"
      12             : 
      13             : #include "mozilla/ArrayUtils.h"
      14             : #include "mozilla/RangedPtr.h"
      15             : #include "mozilla/Unused.h"
      16             : 
      17             : #include <string.h>
      18             : 
      19             : #include "jscntxt.h"
      20             : #include "jsstr.h"
      21             : #include "jstypes.h"
      22             : 
      23             : #include "gc/Marking.h"
      24             : #include "vm/Symbol.h"
      25             : #include "vm/Xdr.h"
      26             : 
      27             : #include "jscntxtinlines.h"
      28             : #include "jscompartmentinlines.h"
      29             : #include "jsobjinlines.h"
      30             : 
      31             : #include "gc/AtomMarking-inl.h"
      32             : #include "vm/String-inl.h"
      33             : 
      34             : using namespace js;
      35             : using namespace js::gc;
      36             : 
      37             : using mozilla::ArrayEnd;
      38             : using mozilla::ArrayLength;
      39             : using mozilla::Maybe;
      40             : using mozilla::Nothing;
      41             : using mozilla::RangedPtr;
      42             : 
      43             : const char*
      44           0 : js::AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes)
      45             : {
      46           0 :     JSString* str = QuoteString(cx, atom, 0);
      47           0 :     if (!str)
      48           0 :         return nullptr;
      49           0 :     return bytes->encodeLatin1(cx, str);
      50             : }
      51             : 
      52             : #define DEFINE_PROTO_STRING(name,code,init,clasp) const char js_##name##_str[] = #name;
      53             : JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
      54             : #undef DEFINE_PROTO_STRING
      55             : 
      56             : #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
      57             : FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
      58             : #undef CONST_CHAR_STR
      59             : 
      60             : /* Constant strings that are not atomized. */
      61             : const char js_getter_str[]          = "getter";
      62             : const char js_send_str[]            = "send";
      63             : const char js_setter_str[]          = "setter";
      64             : 
      65             : // Use a low initial capacity for atom hash tables to avoid penalizing runtimes
      66             : // which create a small number of atoms.
      67             : static const uint32_t JS_STRING_HASH_COUNT = 64;
      68             : 
      69             : MOZ_ALWAYS_INLINE AtomSet::Ptr
      70      124762 : js::FrozenAtomSet::readonlyThreadsafeLookup(const AtomSet::Lookup& l) const
      71             : {
      72      124762 :     return mSet->readonlyThreadsafeLookup(l);
      73             : }
      74             : 
      75             : struct CommonNameInfo
      76             : {
      77             :     const char* str;
      78             :     size_t length;
      79             : };
      80             : 
      81             : bool
      82           4 : JSRuntime::initializeAtoms(JSContext* cx)
      83             : {
      84           4 :     atoms_ = cx->new_<AtomSet>();
      85           4 :     if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
      86           0 :         return false;
      87             : 
      88             :     // |permanentAtoms| hasn't been created yet.
      89           4 :     MOZ_ASSERT(!permanentAtoms);
      90             : 
      91           4 :     if (parentRuntime) {
      92           1 :         staticStrings = parentRuntime->staticStrings;
      93           1 :         commonNames = parentRuntime->commonNames;
      94           1 :         emptyString = parentRuntime->emptyString;
      95           1 :         permanentAtoms = parentRuntime->permanentAtoms;
      96           1 :         wellKnownSymbols = parentRuntime->wellKnownSymbols;
      97           1 :         return true;
      98             :     }
      99             : 
     100           3 :     staticStrings = cx->new_<StaticStrings>();
     101           3 :     if (!staticStrings || !staticStrings->init(cx))
     102           0 :         return false;
     103             : 
     104             :     static const CommonNameInfo cachedNames[] = {
     105             : #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
     106             :         FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
     107             : #undef COMMON_NAME_INFO
     108             : #define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
     109             :         JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
     110             : #undef COMMON_NAME_INFO
     111             : #define COMMON_NAME_INFO(name) { #name, sizeof(#name) - 1 },
     112             :         JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
     113             : #undef COMMON_NAME_INFO
     114             : #define COMMON_NAME_INFO(name) { "Symbol." #name, sizeof("Symbol." #name) - 1 },
     115             :         JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
     116             : #undef COMMON_NAME_INFO
     117             :     };
     118             : 
     119           3 :     commonNames = cx->new_<JSAtomState>();
     120           3 :     if (!commonNames)
     121           0 :         return false;
     122             : 
     123           3 :     ImmutablePropertyNamePtr* names = reinterpret_cast<ImmutablePropertyNamePtr*>(commonNames.ref());
     124        1428 :     for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
     125        1425 :         JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, PinAtom);
     126        1425 :         if (!atom)
     127           0 :             return false;
     128        1425 :         names->init(atom->asPropertyName());
     129             :     }
     130           3 :     MOZ_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
     131             : 
     132           3 :     emptyString = commonNames->empty;
     133             : 
     134             :     // Create the well-known symbols.
     135           3 :     wellKnownSymbols = cx->new_<WellKnownSymbols>();
     136           3 :     if (!wellKnownSymbols)
     137           0 :         return false;
     138             : 
     139           3 :     ImmutablePropertyNamePtr* descriptions = commonNames->wellKnownSymbolDescriptions();
     140           3 :     ImmutableSymbolPtr* symbols = reinterpret_cast<ImmutableSymbolPtr*>(wellKnownSymbols.ref());
     141          39 :     for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
     142          36 :         JS::Symbol* symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]);
     143          36 :         if (!symbol) {
     144           0 :             ReportOutOfMemory(cx);
     145           0 :             return false;
     146             :         }
     147          36 :         symbols[i].init(symbol);
     148             :     }
     149             : 
     150           3 :     return true;
     151             : }
     152             : 
     153             : void
     154           0 : JSRuntime::finishAtoms()
     155             : {
     156           0 :     js_delete(atoms_.ref());
     157             : 
     158           0 :     if (!parentRuntime) {
     159           0 :         js_delete(staticStrings.ref());
     160           0 :         js_delete(commonNames.ref());
     161           0 :         js_delete(permanentAtoms.ref());
     162           0 :         js_delete(wellKnownSymbols.ref());
     163             :     }
     164             : 
     165           0 :     atoms_ = nullptr;
     166           0 :     staticStrings = nullptr;
     167           0 :     commonNames = nullptr;
     168           0 :     permanentAtoms = nullptr;
     169           0 :     wellKnownSymbols = nullptr;
     170           0 :     emptyString = nullptr;
     171           0 : }
     172             : 
     173             : static inline void
     174           1 : TracePinnedAtoms(JSTracer* trc, const AtomSet& atoms)
     175             : {
     176       38981 :     for (auto r = atoms.all(); !r.empty(); r.popFront()) {
     177       38980 :         const AtomStateEntry& entry = r.front();
     178       38980 :         if (entry.isPinned()) {
     179        4802 :             JSAtom* atom = entry.asPtrUnbarriered();
     180        4802 :             TraceRoot(trc, &atom, "interned_atom");
     181        4802 :             MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
     182             :         }
     183             :     }
     184           1 : }
     185             : 
     186             : void
     187           1 : js::TraceAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
     188             : {
     189           1 :     JSRuntime* rt = trc->runtime();
     190             : 
     191           1 :     if (rt->atomsAreFinished())
     192           0 :         return;
     193             : 
     194           1 :     TracePinnedAtoms(trc, rt->atoms(lock));
     195           1 :     if (rt->atomsAddedWhileSweeping())
     196           0 :         TracePinnedAtoms(trc, *rt->atomsAddedWhileSweeping());
     197             : }
     198             : 
     199             : void
     200           1 : js::TracePermanentAtoms(JSTracer* trc)
     201             : {
     202           1 :     JSRuntime* rt = trc->runtime();
     203             : 
     204             :     // Permanent atoms only need to be traced in the runtime which owns them.
     205           1 :     if (rt->parentRuntime)
     206           0 :         return;
     207             : 
     208             :     // Static strings are not included in the permanent atoms table.
     209           1 :     if (rt->staticStrings)
     210           1 :         rt->staticStrings->trace(trc);
     211             : 
     212           1 :     if (rt->permanentAtoms) {
     213        2891 :         for (FrozenAtomSet::Range r(rt->permanentAtoms->all()); !r.empty(); r.popFront()) {
     214        2890 :             const AtomStateEntry& entry = r.front();
     215             : 
     216        2890 :             JSAtom* atom = entry.asPtrUnbarriered();
     217        2890 :             TraceProcessGlobalRoot(trc, atom, "permanent_table");
     218             :         }
     219             :     }
     220             : }
     221             : 
     222             : void
     223           1 : js::TraceWellKnownSymbols(JSTracer* trc)
     224             : {
     225           1 :     JSRuntime* rt = trc->runtime();
     226             : 
     227           1 :     if (rt->parentRuntime)
     228           0 :         return;
     229             : 
     230           1 :     if (WellKnownSymbols* wks = rt->wellKnownSymbols) {
     231          13 :         for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++)
     232          12 :             TraceProcessGlobalRoot(trc, wks->get(i).get(), "well_known_symbol");
     233             :     }
     234             : }
     235             : 
     236             : bool
     237           3 : JSRuntime::transformToPermanentAtoms(JSContext* cx)
     238             : {
     239           3 :     MOZ_ASSERT(!parentRuntime);
     240             : 
     241             :     // All static strings were created as permanent atoms, now move the contents
     242             :     // of the atoms table into permanentAtoms and mark each as permanent.
     243             : 
     244           3 :     MOZ_ASSERT(!permanentAtoms);
     245           3 :     permanentAtoms = cx->new_<FrozenAtomSet>(atoms_);   // takes ownership of atoms_
     246             : 
     247           3 :     atoms_ = cx->new_<AtomSet>();
     248           3 :     if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
     249           0 :         return false;
     250             : 
     251        8673 :     for (FrozenAtomSet::Range r(permanentAtoms->all()); !r.empty(); r.popFront()) {
     252        8670 :         AtomStateEntry entry = r.front();
     253        8670 :         JSAtom* atom = entry.asPtr(cx);
     254        8670 :         atom->morphIntoPermanentAtom();
     255             :     }
     256             : 
     257           3 :     return true;
     258             : }
     259             : 
     260             : static inline AtomSet::Ptr
     261      487368 : LookupAtomState(JSRuntime* rt, const AtomHasher::Lookup& lookup)
     262             : {
     263      487368 :     MOZ_ASSERT(rt->currentThreadHasExclusiveAccess());
     264             : 
     265      487368 :     AtomSet::Ptr p = rt->unsafeAtoms().lookup(lookup); // Safe because we hold the lock.
     266      487368 :     if (!p && rt->atomsAddedWhileSweeping())
     267           0 :         p = rt->atomsAddedWhileSweeping()->lookup(lookup);
     268      487367 :     return p;
     269             : }
     270             : 
     271             : bool
     272       32096 : AtomIsPinned(JSContext* cx, JSAtom* atom)
     273             : {
     274             :     /* We treat static strings as interned because they're never collected. */
     275       32096 :     if (StaticStrings::isStatic(atom))
     276          24 :         return true;
     277             : 
     278       64144 :     AtomHasher::Lookup lookup(atom);
     279             : 
     280             :     /* Likewise, permanent strings are considered to be interned. */
     281       32072 :     MOZ_ASSERT(cx->isPermanentAtomsInitialized());
     282       32072 :     AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
     283       32072 :     if (p)
     284         676 :         return true;
     285             : 
     286       62792 :     AutoLockForExclusiveAccess lock(cx);
     287             : 
     288       31396 :     p = LookupAtomState(cx->runtime(), lookup);
     289       31396 :     if (!p)
     290           0 :         return false;
     291             : 
     292       31396 :     return p->isPinned();
     293             : }
     294             : 
     295             : #ifdef DEBUG
     296             : 
     297             : bool
     298      455925 : AtomIsPinnedInRuntime(JSRuntime* rt, JSAtom* atom)
     299             : {
     300      911883 :     Maybe<AutoLockForExclusiveAccess> lock;
     301      455926 :     if (!rt->currentThreadHasExclusiveAccess())
     302      454644 :         lock.emplace(rt);
     303             : 
     304      911913 :     AtomHasher::Lookup lookup(atom);
     305             : 
     306      455958 :     AtomSet::Ptr p = LookupAtomState(rt, lookup);
     307      455957 :     MOZ_ASSERT(p);
     308             : 
     309      911913 :     return p->isPinned();
     310             : }
     311             : 
     312             : #endif // DEBUG
     313             : 
     314             : /* |tbchars| must not point into an inline or short string. */
     315             : template <typename CharT>
     316             : MOZ_ALWAYS_INLINE
     317             : static JSAtom*
     318      448576 : AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
     319             :                     const Maybe<uint32_t>& indexValue)
     320             : {
     321      448576 :     if (JSAtom* s = cx->staticStrings().lookup(tbchars, length))
     322       23859 :         return s;
     323             : 
     324      849417 :     AtomHasher::Lookup lookup(tbchars, length);
     325             : 
     326             :     // Try the per-Zone cache first. If we find the atom there we can avoid the
     327             :     // atoms lock, the markAtom call, and the multiple HashSet lookups below.
     328             :     // We don't use the per-Zone cache if we want a pinned atom: handling that
     329             :     // is more complicated and pinning atoms is relatively uncommon.
     330      424718 :     Zone* zone = cx->zone();
     331      849421 :     Maybe<AtomSet::AddPtr> zonePtr;
     332      424718 :     if (MOZ_LIKELY(zone && pin == DoNotPinAtom)) {
     333      403485 :         zonePtr.emplace(zone->atomCache().lookupForAdd(lookup));
     334      403486 :         if (zonePtr.ref()) {
     335             :             // The cache is purged on GC so if we're in the middle of an
     336             :             // incremental GC we should have barriered the atom when we put
     337             :             // it in the cache.
     338      322785 :             JSAtom* atom = zonePtr.ref()->asPtrUnbarriered();
     339      322784 :             MOZ_ASSERT(AtomIsMarked(zone, atom));
     340      322771 :             return atom;
     341             :         }
     342             :     }
     343             : 
     344             :     // Note: when this function is called while the permanent atoms table is
     345             :     // being initialized (in initializeAtoms()), |permanentAtoms| is not yet
     346             :     // initialized so this lookup is always skipped. Only once
     347             :     // transformToPermanentAtoms() is called does |permanentAtoms| get
     348             :     // initialized and then this lookup will go ahead.
     349      101932 :     if (cx->isPermanentAtomsInitialized()) {
     350       92677 :         AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
     351       92676 :         if (pp) {
     352        6677 :             JSAtom* atom = pp->asPtr(cx);
     353        6677 :             if (zonePtr)
     354        6130 :                 mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
     355        6677 :             return atom;
     356             :         }
     357             :     }
     358             : 
     359             :     // Validate the length before taking the exclusive access lock, as throwing
     360             :     // an exception here may reenter this code.
     361       95254 :     if (MOZ_UNLIKELY(!JSString::validateLength(cx, length)))
     362           0 :         return nullptr;
     363             : 
     364      190509 :     AutoLockForExclusiveAccess lock(cx);
     365             : 
     366       95255 :     JSRuntime* rt = cx->runtime();
     367       95255 :     AtomSet& atoms = rt->atoms(lock);
     368       95255 :     AtomSet* atomsAddedWhileSweeping = rt->atomsAddedWhileSweeping();
     369       95255 :     AtomSet::AddPtr p;
     370             : 
     371       95255 :     if (!atomsAddedWhileSweeping) {
     372       95255 :         p = atoms.lookupForAdd(lookup);
     373             :     } else {
     374             :         // We're currently sweeping the main atoms table and all new atoms will
     375             :         // be added to a secondary table. Check this first.
     376           0 :         MOZ_ASSERT(rt->atomsZone(lock)->isGCSweeping());
     377           0 :         p = atomsAddedWhileSweeping->lookupForAdd(lookup);
     378             : 
     379             :         // If that fails check the main table but check if any atom found there
     380             :         // is dead.
     381           0 :         if (!p) {
     382           0 :             if (AtomSet::AddPtr p2 = atoms.lookupForAdd(lookup)) {
     383           0 :                 JSAtom* atom = p2->asPtrUnbarriered();
     384           0 :                 if (!IsAboutToBeFinalizedUnbarriered(&atom))
     385           0 :                     p = p2;
     386             :             }
     387             :         }
     388             :     }
     389             : 
     390       95255 :     if (p) {
     391       32693 :         JSAtom* atom = p->asPtr(cx);
     392       32693 :         p->setPinned(bool(pin));
     393       32693 :         cx->atomMarking().inlinedMarkAtom(cx, atom);
     394       32693 :         if (zonePtr)
     395       18439 :             mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
     396       32693 :         return atom;
     397             :     }
     398             : 
     399             :     JSAtom* atom;
     400             :     {
     401      125124 :         AutoAtomsCompartment ac(cx, lock);
     402             : 
     403       62562 :         JSFlatString* flat = NewStringCopyN<NoGC>(cx, tbchars, length);
     404       62562 :         if (!flat) {
     405             :             // Grudgingly forgo last-ditch GC. The alternative would be to release
     406             :             // the lock, manually GC here, and retry from the top. If you fix this,
     407             :             // please also fix or comment the similar case in Symbol::new_.
     408           0 :             ReportOutOfMemory(cx);
     409           0 :             return nullptr;
     410             :         }
     411             : 
     412       62562 :         atom = flat->morphAtomizedStringIntoAtom(lookup.hash);
     413       62562 :         MOZ_ASSERT(atom->hash() == lookup.hash);
     414             : 
     415       62562 :         if (indexValue)
     416         429 :             atom->maybeInitializeIndex(*indexValue, true);
     417             : 
     418             :         // We have held the lock since looking up p, and the operations we've done
     419             :         // since then can't GC; therefore the atoms table has not been modified and
     420             :         // p is still valid.
     421       62562 :         AtomSet* addSet = atomsAddedWhileSweeping ? atomsAddedWhileSweeping : &atoms;
     422       62562 :         if (!addSet->add(p, AtomStateEntry(atom, bool(pin)))) {
     423           0 :             ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
     424           0 :             return nullptr;
     425             :         }
     426             :     }
     427             : 
     428       62562 :     cx->atomMarking().inlinedMarkAtom(cx, atom);
     429       62562 :     if (zonePtr)
     430       56130 :         mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
     431       62562 :     return atom;
     432             : }
     433             : 
     434             : template JSAtom*
     435             : AtomizeAndCopyChars(JSContext* cx, const char16_t* tbchars, size_t length, PinningBehavior pin,
     436             :                     const Maybe<uint32_t>& indexValue);
     437             : 
     438             : template JSAtom*
     439             : AtomizeAndCopyChars(JSContext* cx, const Latin1Char* tbchars, size_t length, PinningBehavior pin,
     440             :                     const Maybe<uint32_t>& indexValue);
     441             : 
     442             : JSAtom*
     443        9975 : js::AtomizeString(JSContext* cx, JSString* str,
     444             :                   js::PinningBehavior pin /* = js::DoNotPinAtom */)
     445             : {
     446        9975 :     if (str->isAtom()) {
     447        3155 :         JSAtom& atom = str->asAtom();
     448             :         /* N.B. static atoms are effectively always interned. */
     449        3155 :         if (pin != PinAtom || js::StaticStrings::isStatic(&atom))
     450        3141 :             return &atom;
     451             : 
     452          28 :         AtomHasher::Lookup lookup(&atom);
     453             : 
     454             :         /* Likewise, permanent atoms are always interned. */
     455          14 :         MOZ_ASSERT(cx->isPermanentAtomsInitialized());
     456          14 :         AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
     457          14 :         if (p)
     458           0 :             return &atom;
     459             : 
     460          28 :         AutoLockForExclusiveAccess lock(cx);
     461             : 
     462          14 :         p = LookupAtomState(cx->runtime(), lookup);
     463          14 :         MOZ_ASSERT(p); /* Non-static atom must exist in atom state set. */
     464          14 :         MOZ_ASSERT(p->asPtrUnbarriered() == &atom);
     465          14 :         MOZ_ASSERT(pin == PinAtom);
     466          14 :         p->setPinned(bool(pin));
     467          14 :         return &atom;
     468             :     }
     469             : 
     470        6820 :     JSLinearString* linear = str->ensureLinear(cx);
     471        6820 :     if (!linear)
     472           0 :         return nullptr;
     473             : 
     474       13640 :     Maybe<uint32_t> indexValue;
     475        6820 :     if (str->hasIndexValue())
     476           0 :         indexValue.emplace(str->getIndexValue());
     477             : 
     478       13640 :     JS::AutoCheckCannotGC nogc;
     479        6820 :     return linear->hasLatin1Chars()
     480        7113 :            ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin, indexValue)
     481        7113 :            : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin, indexValue);
     482             : }
     483             : 
     484             : JSAtom*
     485      105571 : js::Atomize(JSContext* cx, const char* bytes, size_t length, PinningBehavior pin,
     486             :             const Maybe<uint32_t>& indexValue)
     487             : {
     488      211142 :     CHECK_REQUEST(cx);
     489             : 
     490      105570 :     const Latin1Char* chars = reinterpret_cast<const Latin1Char*>(bytes);
     491      211141 :     return AtomizeAndCopyChars(cx, chars, length, pin, indexValue);
     492             : }
     493             : 
     494             : template <typename CharT>
     495             : JSAtom*
     496      336185 : js::AtomizeChars(JSContext* cx, const CharT* chars, size_t length, PinningBehavior pin)
     497             : {
     498      672373 :     CHECK_REQUEST(cx);
     499      672374 :     return AtomizeAndCopyChars(cx, chars, length, pin, Nothing());
     500             : }
     501             : 
     502             : template JSAtom*
     503             : js::AtomizeChars(JSContext* cx, const Latin1Char* chars, size_t length, PinningBehavior pin);
     504             : 
     505             : template JSAtom*
     506             : js::AtomizeChars(JSContext* cx, const char16_t* chars, size_t length, PinningBehavior pin);
     507             : 
     508             : JSAtom*
     509           0 : js::AtomizeUTF8Chars(JSContext* cx, const char* utf8Chars, size_t utf8ByteLength)
     510             : {
     511             :     // This could be optimized to hand the char16_t's directly to the JSAtom
     512             :     // instead of making a copy. UTF8CharsToNewTwoByteCharsZ should be
     513             :     // refactored to take an JSContext so that this function could also.
     514             : 
     515           0 :     UTF8Chars utf8(utf8Chars, utf8ByteLength);
     516             : 
     517             :     size_t length;
     518           0 :     UniqueTwoByteChars chars(JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get());
     519           0 :     if (!chars)
     520           0 :         return nullptr;
     521             : 
     522           0 :     return AtomizeChars(cx, chars.get(), length);
     523             : }
     524             : 
     525             : bool
     526           0 : js::IndexToIdSlow(JSContext* cx, uint32_t index, MutableHandleId idp)
     527             : {
     528           0 :     MOZ_ASSERT(index > JSID_INT_MAX);
     529             : 
     530             :     char16_t buf[UINT32_CHAR_BUFFER_LENGTH];
     531           0 :     RangedPtr<char16_t> end(ArrayEnd(buf), buf, ArrayEnd(buf));
     532           0 :     RangedPtr<char16_t> start = BackfillIndexInCharBuffer(index, end);
     533             : 
     534           0 :     JSAtom* atom = AtomizeChars(cx, start.get(), end - start);
     535           0 :     if (!atom)
     536           0 :         return false;
     537             : 
     538           0 :     idp.set(JSID_FROM_BITS((size_t)atom));
     539           0 :     return true;
     540             : }
     541             : 
     542             : template <AllowGC allowGC>
     543             : static JSAtom*
     544          34 : ToAtomSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
     545             : {
     546          34 :     MOZ_ASSERT(!arg.isString());
     547             : 
     548          34 :     Value v = arg;
     549          34 :     if (!v.isPrimitive()) {
     550           0 :         MOZ_ASSERT(!cx->helperThread());
     551             :         if (!allowGC)
     552           0 :             return nullptr;
     553           0 :         RootedValue v2(cx, v);
     554           0 :         if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
     555           0 :             return nullptr;
     556           0 :         v = v2;
     557             :     }
     558             : 
     559          34 :     if (v.isString()) {
     560           0 :         JSAtom* atom = AtomizeString(cx, v.toString());
     561           0 :         if (!allowGC && !atom)
     562           0 :             cx->recoverFromOutOfMemory();
     563           0 :         return atom;
     564             :     }
     565          34 :     if (v.isInt32()) {
     566           0 :         JSAtom* atom = Int32ToAtom(cx, v.toInt32());
     567           0 :         if (!allowGC && !atom)
     568           0 :             cx->recoverFromOutOfMemory();
     569           0 :         return atom;
     570             :     }
     571          34 :     if (v.isDouble()) {
     572          25 :         JSAtom* atom = NumberToAtom(cx, v.toDouble());
     573           0 :         if (!allowGC && !atom)
     574           0 :             cx->recoverFromOutOfMemory();
     575          25 :         return atom;
     576             :     }
     577           9 :     if (v.isBoolean())
     578           2 :         return v.toBoolean() ? cx->names().true_ : cx->names().false_;
     579           7 :     if (v.isNull())
     580           0 :         return cx->names().null;
     581           7 :     if (v.isSymbol()) {
     582           0 :         MOZ_ASSERT(!cx->helperThread());
     583             :         if (allowGC) {
     584           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     585             :                                       JSMSG_SYMBOL_TO_STRING);
     586             :         }
     587           0 :         return nullptr;
     588             :     }
     589           7 :     MOZ_ASSERT(v.isUndefined());
     590           7 :     return cx->names().undefined;
     591             : }
     592             : 
     593             : template <AllowGC allowGC>
     594             : JSAtom*
     595       18930 : js::ToAtom(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v)
     596             : {
     597       18930 :     if (!v.isString())
     598          34 :         return ToAtomSlow<allowGC>(cx, v);
     599             : 
     600       18896 :     JSString* str = v.toString();
     601       18896 :     if (str->isAtom())
     602       14339 :         return &str->asAtom();
     603             : 
     604        4557 :     JSAtom* atom = AtomizeString(cx, str);
     605           1 :     if (!atom && !allowGC) {
     606           0 :         MOZ_ASSERT_IF(!cx->helperThread(), cx->isThrowingOutOfMemory());
     607           0 :         cx->recoverFromOutOfMemory();
     608             :     }
     609        4557 :     return atom;
     610             : }
     611             : 
     612             : template JSAtom*
     613             : js::ToAtom<CanGC>(JSContext* cx, HandleValue v);
     614             : 
     615             : template JSAtom*
     616             : js::ToAtom<NoGC>(JSContext* cx, const Value& v);
     617             : 
     618             : template<XDRMode mode>
     619             : bool
     620      185709 : js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
     621             : {
     622             :     if (mode == XDR_ENCODE) {
     623             :         static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
     624       44010 :         uint32_t length = atomp->length();
     625       44010 :         uint32_t lengthAndEncoding = (length << 1) | uint32_t(atomp->hasLatin1Chars());
     626       44010 :         if (!xdr->codeUint32(&lengthAndEncoding))
     627           0 :             return false;
     628             : 
     629       88020 :         JS::AutoCheckCannotGC nogc;
     630       44010 :         return atomp->hasLatin1Chars()
     631       44010 :                ? xdr->codeChars(atomp->latin1Chars(nogc), length)
     632       44010 :                : xdr->codeChars(const_cast<char16_t*>(atomp->twoByteChars(nogc)), length);
     633             :     }
     634             : 
     635             :     /* Avoid JSString allocation for already existing atoms. See bug 321985. */
     636             :     uint32_t lengthAndEncoding;
     637      141699 :     if (!xdr->codeUint32(&lengthAndEncoding))
     638           0 :         return false;
     639             : 
     640      141700 :     uint32_t length = lengthAndEncoding >> 1;
     641      141700 :     bool latin1 = lengthAndEncoding & 0x1;
     642             : 
     643      141700 :     JSContext* cx = xdr->cx();
     644             :     JSAtom* atom;
     645      141700 :     if (latin1) {
     646      141692 :         const Latin1Char* chars = nullptr;
     647      141692 :         if (length)
     648      141301 :             chars = reinterpret_cast<const Latin1Char*>(xdr->buf.read(length));
     649      141694 :         atom = AtomizeChars(cx, chars, length);
     650             :     } else {
     651             : #if MOZ_LITTLE_ENDIAN
     652             :         /* Directly access the little endian chars in the XDR buffer. */
     653           8 :         const char16_t* chars = nullptr;
     654           8 :         if (length)
     655           8 :             chars = reinterpret_cast<const char16_t*>(xdr->buf.read(length * sizeof(char16_t)));
     656           8 :         atom = AtomizeChars(cx, chars, length);
     657             : #else
     658             :         /*
     659             :          * We must copy chars to a temporary buffer to convert between little and
     660             :          * big endian data.
     661             :          */
     662             :         char16_t* chars;
     663             :         char16_t stackChars[256];
     664             :         if (length <= ArrayLength(stackChars)) {
     665             :             chars = stackChars;
     666             :         } else {
     667             :             /*
     668             :              * This is very uncommon. Don't use the tempLifoAlloc arena for this as
     669             :              * most allocations here will be bigger than tempLifoAlloc's default
     670             :              * chunk size.
     671             :              */
     672             :             chars = cx->pod_malloc<char16_t>(length);
     673             :             if (!chars)
     674             :                 return false;
     675             :         }
     676             : 
     677             :         JS_ALWAYS_TRUE(xdr->codeChars(chars, length));
     678             :         atom = AtomizeChars(cx, chars, length);
     679             :         if (chars != stackChars)
     680             :             js_free(chars);
     681             : #endif /* !MOZ_LITTLE_ENDIAN */
     682             :     }
     683             : 
     684      141700 :     if (!atom)
     685           0 :         return false;
     686      141700 :     atomp.set(atom);
     687      141700 :     return true;
     688             : }
     689             : 
     690             : template bool
     691             : js::XDRAtom(XDRState<XDR_ENCODE>* xdr, MutableHandleAtom atomp);
     692             : 
     693             : template bool
     694             : js::XDRAtom(XDRState<XDR_DECODE>* xdr, MutableHandleAtom atomp);

Generated by: LCOV version 1.13