MOZ_ALWAYS_INLINE static JSAtom * AtomizeAndCopyChars(ExclusiveContext *cx, const CharT *tbchars, size_t length, InternBehavior ib) { if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) return s; AtomHasher::Lookup lookup(tbchars, length); // Note: when this function is called while the permanent atoms table is // being initialized (in initializeAtoms()), |permanentAtoms| is not yet // initialized so this lookup is always skipped. Only once // transformToPermanentAtoms() is called does |permanentAtoms| get // initialized and then this lookup will go ahead. if (cx->isPermanentAtomsInitialized()) { AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); if (pp) return pp->asPtr(); } AutoLockForExclusiveAccess lock(cx); AtomSet& atoms = cx->atoms(); AtomSet::AddPtr p = atoms.lookupForAdd(lookup); if (p) { JSAtom *atom = p->asPtr(); p->setTagged(bool(ib)); return atom; } AutoCompartment ac(cx, cx->atomsCompartment()); JSFlatString *flat = NewStringCopyN<NoGC>(cx, tbchars, length); if (!flat) { // Grudgingly forgo last-ditch GC. The alternative would be to release // the lock, manually GC here, and retry from the top. If you fix this, // please also fix or comment the similar case in Symbol::new_. ReportOutOfMemory(cx); return nullptr; } JSAtom *atom = flat->morphAtomizedStringIntoAtom(); // We have held the lock since looking up p, and the operations we've done // since then can't GC; therefore the atoms table has not been modified and // p is still valid. if (!atoms.add(p, AtomStateEntry(atom, bool(ib)))) { ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ return nullptr; } return atom; }
/* |tbchars| must not point into an inline or short string. */ MOZ_ALWAYS_INLINE static JSAtom * AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib) { if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) return s; AtomHasher::Lookup lookup(tbchars, length); AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); if (pp) return pp->asPtr(); /* * If a GC occurs at js_NewStringCopy then |p| will still have the correct * hash, allowing us to avoid rehashing it. Even though the hash is * unchanged, we need to re-lookup the table position because a last-ditch * GC will potentially free some table entries. */ AutoLockForExclusiveAccess lock(cx); AtomSet& atoms = cx->atoms(); AtomSet::AddPtr p = atoms.lookupForAdd(lookup); if (p) { JSAtom *atom = p->asPtr(); p->setTagged(bool(ib)); return atom; } AutoCompartment ac(cx, cx->atomsCompartment()); JSFlatString *flat = js_NewStringCopyN<NoGC>(cx, tbchars, length); if (!flat) { js_ReportOutOfMemory(cx); return nullptr; } JSAtom *atom = flat->morphAtomizedStringIntoAtom(); if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) { js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ return nullptr; } return atom; }
JSAtom * js::AtomizeString(ExclusiveContext *cx, JSString *str, js::InternBehavior ib /* = js::DoNotInternAtom */) { if (str->isAtom()) { JSAtom &atom = str->asAtom(); /* N.B. static atoms are effectively always interned. */ if (ib != InternAtom || js::StaticStrings::isStatic(&atom)) return &atom; AutoLockForExclusiveAccess lock(cx); AtomSet::Ptr p = cx->atoms().lookup(AtomHasher::Lookup(&atom)); JS_ASSERT(p); /* Non-static atom must exist in atom state set. */ JS_ASSERT(p->asPtr() == &atom); JS_ASSERT(ib == InternAtom); p->setTagged(bool(ib)); return &atom; } const jschar *chars; if (str->isLinear()) { chars = str->asLinear().chars(); } else { if (!cx->shouldBeJSContext()) return NULL; chars = str->getChars(cx->asJSContext()); if (!chars) return NULL; } if (JSAtom *atom = AtomizeAndCopyChars<NoGC>(cx, chars, str->length(), ib)) return atom; if (!cx->isJSContext() || !allowGC) return NULL; JSLinearString *linear = str->ensureLinear(cx->asJSContext()); if (!linear) return NULL; JS_ASSERT(linear->length() <= JSString::MAX_LENGTH); return AtomizeAndCopyChars<CanGC>(cx, linear->chars(), linear->length(), ib); }
JSAtom * js::AtomizeString(ExclusiveContext *cx, JSString *str, js::InternBehavior ib /* = js::DoNotInternAtom */) { if (str->isAtom()) { JSAtom &atom = str->asAtom(); /* N.B. static atoms are effectively always interned. */ if (ib != InternAtom || js::StaticStrings::isStatic(&atom)) return &atom; AtomHasher::Lookup lookup(&atom); /* Likewise, permanent atoms are always interned. */ MOZ_ASSERT(cx->isPermanentAtomsInitialized()); AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); if (p) return &atom; AutoLockForExclusiveAccess lock(cx); p = cx->atoms().lookup(lookup); MOZ_ASSERT(p); /* Non-static atom must exist in atom state set. */ MOZ_ASSERT(p->asPtr() == &atom); MOZ_ASSERT(ib == InternAtom); p->setTagged(bool(ib)); return &atom; } JSLinearString *linear = str->ensureLinear(cx); if (!linear) return nullptr; JS::AutoCheckCannotGC nogc; return linear->hasLatin1Chars() ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), ib) : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), ib); }
UnrootedAtom js::AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib /* = js::DoNotInternAtom */) { AssertCanGC(); if (str->isAtom()) { JSAtom &atom = str->asAtom(); /* N.B. static atoms are effectively always interned. */ if (ib != InternAtom || js::StaticStrings::isStatic(&atom)) return &atom; AtomSet::Ptr p = cx->runtime->atoms.lookup(AtomHasher::Lookup(&atom)); JS_ASSERT(p); /* Non-static atom must exist in atom state set. */ JS_ASSERT(p->asPtr() == &atom); JS_ASSERT(ib == InternAtom); p->setTagged(bool(ib)); return &atom; } const jschar *chars = str->getChars(cx); if (!chars) return NULL; if (JSAtom *atom = AtomizeAndCopyChars<NoGC>(cx, chars, str->length(), ib)) return atom; if (!allowGC) return NULL; JSLinearString *linear = str->ensureLinear(cx); if (!linear) return NULL; JS_ASSERT(linear->length() <= JSString::MAX_LENGTH); return AtomizeAndCopyChars<CanGC>(cx, linear->chars(), linear->length(), ib); }