Bug 412340: JString stores a flag to indicate that it was atomized. r=brendan a=blocking1.9+

This commit is contained in:
igor@mir2.org 2008-01-18 18:56:51 -08:00
parent d312d51d70
commit d4e057f519
12 changed files with 204 additions and 141 deletions

View File

@ -1694,7 +1694,7 @@ DumpHeap(JSContext *cx, uintN argc, jsval *vp)
static JSBool
DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSAtom *atom;
jsid id;
JSObject *obj2;
JSProperty *prop;
JSBool ok;
@ -1707,19 +1707,18 @@ DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (!JS_ValueToObject(cx, argv[0], &obj))
return JS_FALSE;
argv[0] = OBJECT_TO_JSVAL(obj);
atom = js_ValueToStringAtom(cx, argv[1]);
if (!atom)
if (!js_ValueToStringId(cx, argv[1], &id))
return JS_FALSE;
if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop))
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
return JS_FALSE;
if (!prop) {
ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
JSPROP_EXPORTED, NULL);
} else {
ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
if (ok) {
attrs |= JSPROP_EXPORTED;
ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
}
OBJ_DROP_PROPERTY(cx, obj2, prop);
}

View File

@ -2565,23 +2565,15 @@ JS_DestroyIdArray(JSContext *cx, JSIdArray *ida)
JS_PUBLIC_API(JSBool)
JS_ValueToId(JSContext *cx, jsval v, jsid *idp)
{
JSAtom *atom;
CHECK_REQUEST(cx);
if (JSVAL_IS_INT(v)) {
if (JSVAL_IS_INT(v))
*idp = INT_JSVAL_TO_JSID(v);
} else {
#if JS_HAS_XML_SUPPORT
if (JSVAL_IS_OBJECT(v)) {
*idp = OBJECT_JSVAL_TO_JSID(v);
return JS_TRUE;
}
else if (!JSVAL_IS_PRIMITIVE(v))
*idp = OBJECT_JSVAL_TO_JSID(v);
#endif
atom = js_ValueToStringAtom(cx, v);
if (!atom)
return JS_FALSE;
*idp = ATOM_TO_JSID(atom);
}
else
return js_ValueToStringId(cx, v, idp);
return JS_TRUE;
}
@ -5240,7 +5232,7 @@ JS_GetStringChars(JSString *str)
}
} else {
JSSTRING_CLEAR_MUTABLE(str);
s = str->u.chars;
s = JSFLATSTR_CHARS(str);
}
return s;
}

View File

@ -621,16 +621,16 @@ js_AtomizeDouble(JSContext *cx, jsdouble d)
JSAtom *
js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
{
jsval v;
JSAtomState *state;
JSDHashTable *table;
JSAtomHashEntry *entry;
JSString *key;
uint32 gen;
jsval v;
JS_ASSERT((flags &
~(ATOM_PINNED | ATOM_INTERNED | ATOM_TMPSTR | ATOM_NOCOPY))
== 0);
JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
state = &cx->runtime->atomState;
table = &state->stringAtoms;
@ -638,49 +638,67 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD));
if (!entry)
goto failed_hash_add;
if (entry->keyAndFlags == 0) {
if (entry->keyAndFlags != 0) {
key = (JSString *)ATOM_ENTRY_KEY(entry);
} else {
/*
* We created a new hashtable entry. Unless str is already allocated
* from the GC heap and flat, we have to release state->lock as
* string construction is a complex operation. For example, it can
* trigger GC which may rehash the table and make the entry invalid.
*/
++state->tablegen;
gen = state->tablegen;
JS_UNLOCK(&state->lock, cx);
if (flags & ATOM_TMPSTR) {
if (flags & ATOM_NOCOPY) {
key = js_NewString(cx, str->u.chars, str->length);
if (!key)
return NULL;
/* Transfer ownership of str->chars to GC-controlled string. */
str->u.chars = NULL;
} else {
key = js_NewStringCopyN(cx, str->u.chars, str->length);
if (!key)
return NULL;
}
} else {
JS_ASSERT((flags & ATOM_NOCOPY) == 0);
if (!JS_MakeStringImmutable(cx, str))
return NULL;
if (!(flags & ATOM_TMPSTR) && !JSSTRING_IS_DEPENDENT(str)) {
JSSTRING_CLEAR_MUTABLE(str);
key = str;
}
JS_LOCK(&state->lock, cx);
if (state->tablegen == gen) {
JS_ASSERT(entry->keyAndFlags == 0);
} else {
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
JS_DHASH_ADD));
if (!entry)
goto failed_hash_add;
if (entry->keyAndFlags != 0)
goto finish;
++state->tablegen;
gen = state->tablegen;
JS_UNLOCK(&state->lock, cx);
if (flags & ATOM_TMPSTR) {
if (flags & ATOM_NOCOPY) {
key = js_NewString(cx, JSFLATSTR_CHARS(str),
JSFLATSTR_LENGTH(str));
if (!key)
return NULL;
/* Finish handing off chars to the GC'ed key string. */
str->u.chars = NULL;
} else {
key = js_NewStringCopyN(cx, JSFLATSTR_CHARS(str),
JSFLATSTR_LENGTH(str));
if (!key)
return NULL;
}
} else {
JS_ASSERT(JSSTRING_IS_DEPENDENT(str));
if (!js_UndependString(cx, str))
return NULL;
key = str;
}
JS_LOCK(&state->lock, cx);
if (state->tablegen == gen) {
JS_ASSERT(entry->keyAndFlags == 0);
} else {
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
JS_DHASH_ADD));
if (!entry)
goto failed_hash_add;
if (entry->keyAndFlags != 0)
goto finish;
++state->tablegen;
}
}
INIT_ATOM_ENTRY(entry, key);
JSSTRING_SET_ATOMIZED(key);
}
finish:
ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED));
v = STRING_TO_JSVAL((JSString *)ATOM_ENTRY_KEY(entry));
JS_ASSERT(JSSTRING_IS_ATOMIZED(key));
v = STRING_TO_JSVAL(key);
cx->weakRoots.lastAtom = v;
JS_UNLOCK(&state->lock, cx);
return (JSAtom *)v;
@ -779,15 +797,36 @@ js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp)
return JS_TRUE;
}
JSAtom *
js_ValueToStringAtom(JSContext *cx, jsval v)
JSBool
js_ValueToStringId(JSContext *cx, jsval v, jsid *idp)
{
JSString *str;
JSAtom *atom;
str = js_ValueToString(cx, v);
if (!str)
return NULL;
return js_AtomizeString(cx, str, 0);
/*
* Optimize for the common case where v is an already-atomized string. The
* comment in jsstr.h before the JSSTRING_SET_ATOMIZED macro's definition
* explains why this is thread-safe. The extra rooting via lastAtom (which
* would otherwise be done in js_js_AtomizeString) ensures the caller that
* the resulting id at least weakly rooted.
*/
if (JSVAL_IS_STRING(v)) {
str = JSVAL_TO_STRING(v);
if (JSSTRING_IS_ATOMIZED(str)) {
cx->weakRoots.lastAtom = v;
*idp = ATOM_TO_JSID((JSAtom *) v);
return JS_TRUE;
}
} else {
str = js_ValueToString(cx, v);
if (!str)
return JS_FALSE;
}
atom = js_AtomizeString(cx, str, 0);
if (!atom)
return JS_FALSE;
*idp = ATOM_TO_JSID(atom);
return JS_TRUE;
}
#ifdef DEBUG

View File

@ -416,10 +416,10 @@ JSBool
js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp);
/*
* Convert v to an atomized string.
* Convert v to an atomized string and wrap it as an id.
*/
extern JSAtom *
js_ValueToStringAtom(JSContext *cx, jsval v);
extern JSBool
js_ValueToStringId(JSContext *cx, jsval v, jsid *idp);
#ifdef DEBUG

View File

@ -675,9 +675,9 @@ js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
if (JSID_IS_ATOM(id)) {
atom = JSID_TO_ATOM(id);
} else if (JSID_IS_INT(id)) {
atom = js_ValueToStringAtom(cx, INT_JSID_TO_JSVAL(id));
if (!atom)
if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &id))
return NULL;
atom = JSID_TO_ATOM(id);
} else {
atom = NULL;
}
@ -690,10 +690,9 @@ js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
}
JS_PUBLIC_API(JSBool)
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval idval,
JSWatchPointHandler handler, void *closure)
{
JSAtom *atom;
jsid propid;
JSObject *pobj;
JSProperty *prop;
@ -709,15 +708,10 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
return JS_FALSE;
}
if (JSVAL_IS_INT(id)) {
propid = INT_JSVAL_TO_JSID(id);
atom = NULL;
} else {
atom = js_ValueToStringAtom(cx, id);
if (!atom)
return JS_FALSE;
propid = ATOM_TO_JSID(atom);
}
if (JSVAL_IS_INT(idval))
propid = INT_JSVAL_TO_JSID(idval);
else if (!js_ValueToStringId(cx, idval, &propid))
return JS_FALSE;
if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
return JS_FALSE;
@ -752,8 +746,8 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
flags = sprop->flags;
shortid = sprop->shortid;
} else {
if (!OBJ_GET_PROPERTY(cx, pobj, id, &value) ||
!OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs)) {
if (!OBJ_GET_PROPERTY(cx, pobj, propid, &value) ||
!OBJ_GET_ATTRIBUTES(cx, pobj, propid, prop, &attrs)) {
OBJ_DROP_PROPERTY(cx, pobj, prop);
return JS_FALSE;
}
@ -1522,7 +1516,8 @@ GetAtomTotalSize(JSContext *cx, JSAtom *atom)
nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
if (ATOM_IS_STRING(atom)) {
nbytes += sizeof(JSString);
nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar);
nbytes += (JSFLATSTR_LENGTH(ATOM_TO_STRING(atom)) + 1)
* sizeof(jschar);
} else if (ATOM_IS_DOUBLE(atom)) {
nbytes += sizeof(jsdouble);
}

View File

@ -807,12 +807,11 @@ call_enumerate(JSContext *cx, JSObject *obj)
}
static JSBool
call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
JSObject **objp)
{
JSStackFrame *fp;
JSString *str;
JSAtom *atom;
jsid id;
JSLocalKind localKind;
JSPropertyOp getter, setter;
uintN slot, attrs;
@ -824,15 +823,13 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
JS_ASSERT(fp->fun);
JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee) == fp->fun);
if (!JSVAL_IS_STRING(id))
if (!JSVAL_IS_STRING(idval))
return JS_TRUE;
str = JSVAL_TO_STRING(id);
atom = js_AtomizeString(cx, str, 0);
if (!atom)
if (!js_ValueToStringId(cx, idval, &id))
return JS_FALSE;
localKind = js_LookupLocal(cx, fp->fun, atom, &slot);
localKind = js_LookupLocal(cx, fp->fun, JSID_TO_ATOM(id), &slot);
if (localKind != JSLOCAL_NONE) {
if (localKind == JSLOCAL_ARG) {
JS_ASSERT(slot < fp->fun->nargs);
@ -850,9 +847,9 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
? JSPROP_PERMANENT | JSPROP_READONLY
: JSPROP_PERMANENT;
}
if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), vp[slot],
getter, setter, attrs,
SPROP_HAS_SHORTID, (int) slot, NULL)) {
if (!js_DefineNativeProperty(cx, obj, id, vp[slot], getter, setter,
attrs, SPROP_HAS_SHORTID, (int) slot,
NULL)) {
return JS_FALSE;
}
*objp = obj;
@ -863,10 +860,8 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
* Resolve arguments so that we never store a particular Call object's
* arguments object reference in a Call prototype's |arguments| slot.
*/
atom = cx->runtime->atomState.argumentsAtom;
if (id == ATOM_KEY(atom)) {
if (!js_DefineNativeProperty(cx, obj,
ATOM_TO_JSID(atom), JSVAL_VOID,
if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
NULL, NULL, JSPROP_PERMANENT,
SPROP_HAS_SHORTID, CALL_ARGUMENTS,
NULL)) {

View File

@ -1632,8 +1632,6 @@ js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc)
static JSBool
InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
{
JSAtom *atom;
JS_ASSERT(!JSVAL_IS_INT(idval));
#if JS_HAS_XML_SUPPORT
@ -1649,11 +1647,7 @@ InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
}
#endif
atom = js_ValueToStringAtom(cx, idval);
if (!atom)
return JS_FALSE;
*idp = ATOM_TO_JSID(atom);
return JS_TRUE;
return js_ValueToStringId(cx, idval, idp);
}
/*

View File

@ -2937,11 +2937,11 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
if (JSID_IS_ATOM(id)) { \
JSAtom *atom_ = JSID_TO_ATOM(id); \
JSString *str_ = ATOM_TO_STRING(atom_); \
const jschar *cp_ = str_->u.chars; \
const jschar *cp_ = JSFLATSTR_CHARS(str_); \
JSBool negative_ = (*cp_ == '-'); \
if (negative_) cp_++; \
if (JS7_ISDEC(*cp_)) { \
size_t n_ = str_->length - negative_; \
size_t n_ = JSFLATSTR_LENGTH(str_) - negative_; \
if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1) \
id = CheckForStringIndex(id, cp_, cp_ + n_, negative_); \
} \

View File

@ -6406,7 +6406,7 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
/* XXX fold only if all operands convert to string */
if (pn2->pn_type != TOK_STRING)
return JS_TRUE;
length += ATOM_TO_STRING(pn2->pn_atom)->length;
length += JSFLATSTR_LENGTH(ATOM_TO_STRING(pn2->pn_atom));
}
/* Allocate a new buffer and string descriptor for the result. */
@ -6422,8 +6422,8 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
/* Fill the buffer, advancing chars and recycling kids as we go. */
for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
str2 = ATOM_TO_STRING(pn2->pn_atom);
length2 = str2->length;
js_strncpy(chars, str2->u.chars, length2);
length2 = JSFLATSTR_LENGTH(str2);
js_strncpy(chars, JSFLATSTR_CHARS(str2), length2);
chars += length2;
}
*chars = 0;

View File

@ -110,8 +110,8 @@ js_GetDependentStringChars(JSString *str)
start = js_MinimizeDependentStrings(str, 0, &base);
JS_ASSERT(!JSSTRING_IS_DEPENDENT(base));
JS_ASSERT(start < (base->length & ~JSSTRFLAG_MUTABLE));
return base->u.chars + start;
JS_ASSERT(start < JSFLATSTR_LENGTH(base));
return JSFLATSTR_CHARS(base) + start;
}
const jschar *
@ -120,7 +120,7 @@ js_GetStringChars(JSContext *cx, JSString *str)
if (!js_MakeStringImmutable(cx, str))
return NULL;
JS_ASSERT(!JSSTRING_IS_DEPENDENT(str));
return str->u.chars;
return JSFLATSTR_CHARS(str);
}
JSString *
@ -225,7 +225,7 @@ js_UndependString(JSContext *cx, JSString *str)
#endif
}
return str->u.chars;
return JSFLATSTR_CHARS(str);
}
JSBool
@ -2645,6 +2645,7 @@ js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,
void
js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)
{
jschar *chars;
JSBool valid;
JSStringFinalizeOp finalizer;
@ -2657,14 +2658,15 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)
valid = JS_TRUE;
} else {
/* A stillborn string has null chars, so is not valid. */
valid = (str->u.chars != NULL);
chars = JSFLATSTR_CHARS(str);
valid = (chars != NULL);
if (valid) {
if (IN_UNIT_STRING_SPACE_RT(rt, str->u.chars)) {
JS_ASSERT(rt->unitStrings[*str->u.chars] == str);
if (IN_UNIT_STRING_SPACE_RT(rt, chars)) {
JS_ASSERT(rt->unitStrings[*chars] == str);
JS_ASSERT(type < 0);
rt->unitStrings[*str->u.chars] = NULL;
rt->unitStrings[*chars] = NULL;
} else if (type < 0) {
free(str->u.chars);
free(chars);
} else {
JS_ASSERT((uintN) type < JS_ARRAY_LENGTH(str_finalizers));
finalizer = str_finalizers[type];

View File

@ -70,6 +70,10 @@ JS_BEGIN_EXTERN_C
* such string but extreme care must be taken to ensure that no other code
* relies on the original length of the string.
*
* A flat string with JSSTRFLAG_ATOMIZED set means that the string is hashed
* as an atom. This flag is used to avoid re-hashing of the already-atomized
* string.
*
* When JSSTRFLAG_DEPENDENT is set, the string depends on characters of
* another string strongly referenced by the u.base field. The base member may
* point to another dependent string if JSSTRING_CHARS has not been called
@ -99,59 +103,102 @@ struct JSString {
* Definitions for flags stored in the high order bits of JSString.length.
* JSSTRFLAG_PREFIX and JSSTRFLAG_MUTABLE are two aliases for the same value.
* JSSTRFLAG_PREFIX should be used only if JSSTRFLAG_DEPENDENT is set and
* JSSTRFLAG_MUTABLE should be used only if JSSTRFLAG_DEPENDENT is unset.
* JSSTRFLAG_MUTABLE should be used only if the string is flat and
* JSSTRFLAG_DEPENDENT is unset. JSSTRFLAG_ATOMIZED is used only with the
* flat immutable strings.
*/
#define JSSTRFLAG_BITS 2
#define JSSTRFLAG_SHIFT(flg) ((size_t)(flg) << JSSTRING_LENGTH_BITS)
#define JSSTRFLAG_MASK JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS))
#define JSSTRFLAG_DEPENDENT JSSTRFLAG_SHIFT(1)
#define JSSTRFLAG_PREFIX JSSTRFLAG_SHIFT(2)
#define JSSTRFLAG_MUTABLE JSSTRFLAG_SHIFT(2)
#define JSSTRFLAG_DEPENDENT JSSTRING_BIT(JS_BITS_PER_WORD - 1)
#define JSSTRFLAG_PREFIX JSSTRING_BIT(JS_BITS_PER_WORD - 2)
#define JSSTRFLAG_MUTABLE JSSTRFLAG_PREFIX
#define JSSTRFLAG_ATOMIZED JSSTRING_BIT(JS_BITS_PER_WORD - 3)
#define JSSTRING_LENGTH_BITS (JS_BITS_PER_WORD - 3)
#define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS)
/* Universal JSString type inquiry and accessor macros. */
#define JSSTRING_BIT(n) ((size_t)1 << (n))
#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1)
#define JSSTRING_HAS_FLAG(str,flg) ((str)->length & (flg))
#define JSSTRING_IS_DEPENDENT(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT)
#define JSSTRING_IS_MUTABLE(str) (((str)->length & JSSTRFLAG_MASK) == \
#define JSSTRING_IS_MUTABLE(str) (((str)->length & (JSSTRFLAG_DEPENDENT | \
JSSTRFLAG_MUTABLE)) == \
JSSTRFLAG_MUTABLE)
#define JSSTRING_IS_ATOMIZED(str) (((str)->length & (JSSTRFLAG_DEPENDENT | \
JSSTRFLAG_ATOMIZED)) ==\
JSSTRFLAG_ATOMIZED)
#define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \
? JSSTRDEP_CHARS(str) \
: (str)->u.chars)
: JSFLATSTR_CHARS(str))
#define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \
? JSSTRDEP_LENGTH(str) \
: ((str)->length & ~JSSTRFLAG_MUTABLE))
: JSFLATSTR_LENGTH(str))
#define JSSTRING_CHARS_AND_LENGTH(str, chars_, length_) \
((void)(JSSTRING_IS_DEPENDENT(str) \
? ((length_) = JSSTRDEP_LENGTH(str), \
(chars_) = JSSTRDEP_CHARS(str)) \
: ((length_) = (str)->length & ~JSSTRFLAG_MUTABLE, \
(chars_) = (str)->u.chars)))
: ((length_) = JSFLATSTR_LENGTH(str), \
(chars_) = JSFLATSTR_CHARS(str))))
#define JSSTRING_CHARS_AND_END(str, chars_, end) \
((void)((end) = JSSTRING_IS_DEPENDENT(str) \
? JSSTRDEP_LENGTH(str) + ((chars_) = JSSTRDEP_CHARS(str)) \
: ((str)->length & ~JSSTRFLAG_MUTABLE) + \
((chars_) = (str)->u.chars)))
#define JSSTRING_LENGTH_BITS (sizeof(size_t) * JS_BITS_PER_BYTE \
- JSSTRFLAG_BITS)
#define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS)
: JSFLATSTR_LENGTH(str) + ((chars_) = JSFLATSTR_CHARS(str))))
#define JSSTRING_INIT(str, chars_, length_) \
((void)(JS_ASSERT(((length_) & JSSTRFLAG_MASK) == 0), \
((void)(JS_ASSERT(((length_) & ~JSSTRING_LENGTH_MASK) == 0), \
(str)->length = (length_), (str)->u.chars = (chars_)))
/* Specific mutable string manipulation macros. */
/*
* Specific macro to get the length and characters of a flat string.
*/
#define JSFLATSTR_LENGTH(str) \
(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)), \
(str)->length & JSSTRING_LENGTH_MASK)
#define JSFLATSTR_CHARS(str) \
(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)), (str)->u.chars)
/*
* Specific macros to manipulate atomized and mutable flags. It is safe to use
* these without extra locking due to the following properties:
*
* * We do not have a macro like JSSTRING_CLEAR_ATOMIZED as a string remains
* atomized until the GC collects it.
*
* * A thread may call JSSTRING_SET_MUTABLE only when it is the only thread
* accessing the string until a later call to JSSTRING_CLEAR_MUTABLE.
*
* * Multiple threads can call JSSTRING_CLEAR_MUTABLE but the macro actually
* clears the mutable flag only when the flag is set -- in which case only
* one thread can access the string (see previous property).
*
* Thus, when multiple threads access the string, JSSTRING_SET_ATOMIZED is
* the only macro that can update the length field of the string by changing
* the mutable bit from 0 to 1. We call the macro only after the string has
* been hashed. When some threads in js_ValueToStringId see that the flag is
* set, it knows that the string was atomized.
*
* On the other hand, if the thread sees that the flag is unset, it could be
* seeing a stale value when another thread has just atomized the string and
* set the flag. But this can lead only to an extra call to js_AtomizeString.
* This function would find that the string was already hashed and return it
* with the atomized bit set.
*/
#define JSSTRING_SET_ATOMIZED(str) \
((void)(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)), \
JS_ASSERT(!JSSTRING_IS_MUTABLE(str)), \
(str)->length |= JSSTRFLAG_ATOMIZED))
#define JSSTRING_SET_MUTABLE(str) \
((void)(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)), \
(str)->length |= JSSTRFLAG_MUTABLE))
#define JSSTRING_CLEAR_MUTABLE(str) \
((void)(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)), \
(str)->length &= ~JSSTRFLAG_MUTABLE))
JSSTRING_HAS_FLAG(str, JSSTRFLAG_MUTABLE) && \
((str)->length &= ~JSSTRFLAG_MUTABLE)))
/* Specific dependent string shift/mask accessor and mutator macros. */
#define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS)
@ -188,7 +235,7 @@ struct JSString {
#define JSSTRDEP_CHARS(str) \
(JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \
? js_GetDependentStringChars(str) \
: JSSTRDEP_BASE(str)->u.chars + JSSTRDEP_START(str))
: JSFLATSTR_CHARS(JSSTRDEP_BASE(str)) + JSSTRDEP_START(str))
extern size_t
js_MinimizeDependentStrings(JSString *str, int level, JSString **basep);

View File

@ -7896,7 +7896,7 @@ js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
str = js_NewStringCopyN(cx, chars, len);
if (!str)
return NULL;
chars = str->u.chars;
chars = JSFLATSTR_CHARS(str);
} else {
/*
* Reallocating str (because we know it has no other references)