Bug 802318 - Remove the invalid SkipRoot in AtomizeInline; r=billm

This re-organizes AtomizeInline to separate the TakeOwnership and Copy cases.

--HG--
extra : rebase_source : 2045f8503e7ff0419f992e4268683d1b63d5f094
This commit is contained in:
Terrence Cole 2013-01-07 15:32:01 -08:00
parent d4c786e67e
commit c403cd882a
3 changed files with 104 additions and 75 deletions

View File

@ -236,67 +236,100 @@ enum OwnCharsBehavior
}; };
/* /*
* Callers passing OwnChars have freshly allocated *pchars and thus this * When the jschars reside in a freshly allocated buffer the memory can be used
* memory can be used as a new JSAtom's buffer without copying. When this flag * as a new JSAtom's storage without copying. The contract is that the caller no
* is set, the contract is that callers will free *pchars iff *pchars == NULL. * longer owns the memory and this method is responsible for freeing the memory.
*/ */
JS_ALWAYS_INLINE JS_ALWAYS_INLINE
static JSAtom * static UnrootedAtom
AtomizeInline(JSContext *cx, const jschar **pchars, size_t length, AtomizeAndTakeOwnership(JSContext *cx, StableCharPtr tbchars, size_t length,
InternBehavior ib, OwnCharsBehavior ocb = CopyChars) InternBehavior ib)
{ {
const jschar *chars = *pchars; JS_ASSERT(tbchars[length] == 0);
if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length)) if (UnrootedAtom s = cx->runtime->staticStrings.lookup(tbchars.get(), length)) {
js_free((void*)tbchars.get());
return s; return s;
}
AtomSet &atoms = cx->runtime->atoms; /*
AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length)); * 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.
*/
AtomHasher::Lookup lookup(tbchars.get(), length);
AtomSet::AddPtr p = cx->runtime->atoms.lookupForAdd(lookup);
SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */
if (p) { if (p) {
JSAtom *atom = p->asPtr(); UnrootedAtom atom = p->asPtr();
p->setTagged(bool(ib));
js_free((void*)tbchars.get());
return atom;
}
AutoEnterAtomsCompartment ac(cx);
UnrootedFlatString flat = js_NewString(cx, const_cast<jschar*>(tbchars.get()), length);
if (!flat) {
js_free((void*)tbchars.get());
return UnrootedAtom();
}
UnrootedAtom atom = flat->morphAtomizedStringIntoAtom();
if (!cx->runtime->atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
return UnrootedAtom();
}
return atom;
}
/* |tbchars| must not point into an inline or short string. */
JS_ALWAYS_INLINE
static UnrootedAtom
AtomizeAndCopyStableChars(JSContext *cx, const jschar *tbchars, size_t length, InternBehavior ib)
{
if (UnrootedAtom s = cx->runtime->staticStrings.lookup(tbchars, length))
return s;
/*
* 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.
*/
AtomHasher::Lookup lookup(tbchars, length);
AtomSet::AddPtr p = cx->runtime->atoms.lookupForAdd(lookup);
SkipRoot skipHash(cx, &p); /* Prevent the hash from being poisoned. */
if (p) {
UnrootedAtom atom = p->asPtr();
p->setTagged(bool(ib)); p->setTagged(bool(ib));
return atom; return atom;
} }
AutoEnterAtomsCompartment ac(cx); AutoEnterAtomsCompartment ac(cx);
SkipRoot skip(cx, &chars); UnrootedFlatString flat = js_NewStringCopyN(cx, tbchars, length);
if (!flat)
return UnrootedAtom();
/* Workaround for hash values in AddPtr being inadvertently poisoned. */ UnrootedAtom atom = flat->morphAtomizedStringIntoAtom();
SkipRoot skip2(cx, &p);
JSFlatString *key; if (!cx->runtime->atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
if (ocb == TakeCharOwnership) { JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
key = js_NewString(cx, const_cast<jschar *>(chars), length); return UnrootedAtom();
*pchars = NULL; /* Called should not free *pchars. */
} else {
JS_ASSERT(ocb == CopyChars);
key = js_NewStringCopyN(cx, chars, length);
}
if (!key)
return NULL;
/*
* We have to relookup the key as the last ditch GC invoked from the
* string allocation or OOM handling unlocks the atomsCompartment.
*
* N.B. this avoids recomputing the hash but still has a potential
* (# collisions * # chars) comparison cost in the case of a hash
* collision!
*/
AtomHasher::Lookup lookup(chars, length);
if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry((JSAtom *) key, bool(ib)))) {
JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */
return NULL;
} }
return key->morphAtomizedStringIntoAtom(); return atom;
} }
JSAtom * UnrootedAtom
js::AtomizeString(JSContext *cx, JSString *str, InternBehavior ib) js::AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib /* = js::DoNotInternAtom */)
{ {
AssertCanGC();
if (str->isAtom()) { if (str->isAtom()) {
JSAtom &atom = str->asAtom(); JSAtom &atom = str->asAtom();
/* N.B. static atoms are effectively always interned. */ /* N.B. static atoms are effectively always interned. */
@ -315,60 +348,53 @@ js::AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
if (!stable) if (!stable)
return NULL; return NULL;
const jschar *chars = stable->chars().get(); JS_ASSERT(stable->length() <= JSString::MAX_LENGTH);
size_t length = stable->length(); return AtomizeAndCopyStableChars(cx, stable->chars().get(), stable->length(), ib);
JS_ASSERT(length <= JSString::MAX_LENGTH);
return AtomizeInline(cx, &chars, length, ib);
} }
JSAtom * UnrootedAtom
js::Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib) js::Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib)
{ {
AssertCanGC();
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
if (!JSString::validateLength(cx, length)) if (!JSString::validateLength(cx, length))
return NULL; return NULL;
/* UnrootedAtom atom;
* Avoiding the malloc in InflateString on shorter strings saves us
* over 20,000 malloc calls on mozilla browser startup. This compares to
* only 131 calls where the string is longer than a 31 char (net) buffer.
* The vast majority of atomized strings are already in the hashtable. So
* js::AtomizeString rarely has to copy the temp string we make.
*/
static const unsigned ATOMIZE_BUF_MAX = 32; static const unsigned ATOMIZE_BUF_MAX = 32;
jschar inflated[ATOMIZE_BUF_MAX];
size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
const jschar *chars;
OwnCharsBehavior ocb = CopyChars;
if (length < ATOMIZE_BUF_MAX) { if (length < ATOMIZE_BUF_MAX) {
/*
* Avoiding the malloc in InflateString on shorter strings saves us
* over 20,000 malloc calls on mozilla browser startup. This compares to
* only 131 calls where the string is longer than a 31 char (net) buffer.
* The vast majority of atomized strings are already in the hashtable. So
* js::AtomizeString rarely has to copy the temp string we make.
*/
jschar inflated[ATOMIZE_BUF_MAX];
size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
inflated[inflatedLength] = 0; atom = AtomizeAndCopyStableChars(cx, inflated, inflatedLength, ib);
chars = inflated;
} else { } else {
inflatedLength = length; jschar *tbcharsZ = InflateString(cx, bytes, &length);
chars = InflateString(cx, bytes, &inflatedLength); if (!tbcharsZ)
if (!chars) return UnrootedAtom();
return NULL; atom = AtomizeAndTakeOwnership(cx, StableCharPtr(tbcharsZ, length), length, ib);
ocb = TakeCharOwnership;
} }
JSAtom *atom = AtomizeInline(cx, &chars, inflatedLength, ib, ocb);
if (ocb == TakeCharOwnership && chars)
js_free((void *)chars);
return atom; return atom;
} }
JSAtom * UnrootedAtom
js::AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib) js::AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib)
{ {
AssertCanGC();
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
if (!JSString::validateLength(cx, length)) if (!JSString::validateLength(cx, length))
return NULL; return NULL;
return AtomizeInline(cx, &chars, length, ib); return AtomizeAndCopyStableChars(cx, chars, length, ib);
} }
bool bool

View File

@ -23,6 +23,8 @@
#include "js/HashTable.h" #include "js/HashTable.h"
#include "vm/CommonPropertyNames.h" #include "vm/CommonPropertyNames.h"
ForwardDeclareJS(Atom);
struct JSIdArray { struct JSIdArray {
int length; int length;
js::HeapId vector[1]; /* actually, length jsid words */ js::HeapId vector[1]; /* actually, length jsid words */
@ -79,7 +81,7 @@ class AtomStateEntry
public: public:
AtomStateEntry() : bits(0) {} AtomStateEntry() : bits(0) {}
AtomStateEntry(const AtomStateEntry &other) : bits(other.bits) {} AtomStateEntry(const AtomStateEntry &other) : bits(other.bits) {}
AtomStateEntry(JSAtom *ptr, bool tagged) AtomStateEntry(RawAtom ptr, bool tagged)
: bits(uintptr_t(ptr) | uintptr_t(tagged)) : bits(uintptr_t(ptr) | uintptr_t(tagged))
{ {
JS_ASSERT((uintptr_t(ptr) & 0x1) == 0); JS_ASSERT((uintptr_t(ptr) & 0x1) == 0);
@ -220,15 +222,15 @@ enum InternBehavior
InternAtom = true InternAtom = true
}; };
extern JSAtom * extern UnrootedAtom
Atomize(JSContext *cx, const char *bytes, size_t length, Atomize(JSContext *cx, const char *bytes, size_t length,
js::InternBehavior ib = js::DoNotInternAtom); js::InternBehavior ib = js::DoNotInternAtom);
extern JSAtom * extern UnrootedAtom
AtomizeChars(JSContext *cx, const jschar *chars, size_t length, AtomizeChars(JSContext *cx, const jschar *chars, size_t length,
js::InternBehavior ib = js::DoNotInternAtom); js::InternBehavior ib = js::DoNotInternAtom);
extern JSAtom * extern UnrootedAtom
AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom); AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
inline JSAtom * inline JSAtom *

View File

@ -28,7 +28,8 @@ class JSLinearString;
class JSStableString; class JSStableString;
class JSInlineString; class JSInlineString;
class JSRope; class JSRope;
class JSAtom; ForwardDeclareJS(FlatString);
ForwardDeclareJS(Atom);
namespace js { namespace js {