Bug 391290: mutability flag for strings is stored inside strings. r=brendan

This commit is contained in:
igor@mir2.org 2007-08-15 23:23:06 -07:00
parent a96dc350c6
commit 4773ca1cae
19 changed files with 375 additions and 342 deletions

View File

@ -2515,8 +2515,7 @@ JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type)
str = (JSString *) js_NewGCThing(cx, (uintN) type, sizeof(JSString));
if (!str)
return NULL;
str->length = length;
str->chars = chars;
JSSTRING_INIT(str, chars, length);
return str;
}
@ -2527,7 +2526,7 @@ JS_GetExternalStringGCType(JSRuntime *rt, JSString *str)
if (type >= GCX_EXTERNAL_STRING)
return (intN)type;
JS_ASSERT(type == GCX_STRING || type == GCX_MUTABLE_STRING);
JS_ASSERT(type == GCX_STRING);
return -1;
}
@ -5028,7 +5027,7 @@ JS_NewString(JSContext *cx, char *bytes, size_t nbytes)
return NULL;
/* Free chars (but not bytes, which caller frees on error) if we fail. */
str = js_NewString(cx, chars, length, 0);
str = js_NewString(cx, chars, length);
if (!str) {
JS_free(cx, chars);
return NULL;
@ -5050,7 +5049,7 @@ JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
js = js_InflateString(cx, s, &n);
if (!js)
return NULL;
str = js_NewString(cx, js, n, 0);
str = js_NewString(cx, js, n);
if (!str)
JS_free(cx, js);
return str;
@ -5070,7 +5069,7 @@ JS_NewStringCopyZ(JSContext *cx, const char *s)
js = js_InflateString(cx, s, &n);
if (!js)
return NULL;
str = js_NewString(cx, js, n, 0);
str = js_NewString(cx, js, n);
if (!str)
JS_free(cx, js);
return str;
@ -5092,7 +5091,7 @@ JS_PUBLIC_API(JSString *)
JS_NewUCString(JSContext *cx, jschar *chars, size_t length)
{
CHECK_REQUEST(cx);
return js_NewString(cx, chars, length, 0);
return js_NewString(cx, chars, length);
}
JS_PUBLIC_API(JSString *)
@ -5162,15 +5161,17 @@ JS_GetStringChars(JSString *str)
size = (n + 1) * sizeof(jschar);
s = (jschar *) malloc(size);
if (s) {
js_strncpy(s, JSSTRDEP_CHARS(str), n);
memcpy(s, JSSTRDEP_CHARS(str), n * sizeof *s);
s[n] = 0;
str->length = n;
str->chars = s;
JSSTRING_INIT(str, s, n);
} else {
s = JSSTRDEP_CHARS(str);
}
} else {
JSSTRING_CLEAR_MUTABLE(str);
s = str->u.chars;
}
*js_GetGCThingFlags(str) &= ~GCF_MUTABLE;
return JSSTRING_CHARS(str);
return s;
}
JS_PUBLIC_API(size_t)
@ -5188,8 +5189,14 @@ JS_CompareStrings(JSString *str1, JSString *str2)
JS_PUBLIC_API(JSString *)
JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length)
{
JSString *str;
CHECK_REQUEST(cx);
return js_NewString(cx, chars, length, GCF_MUTABLE);
str = js_NewString(cx, chars, length);
if (!str)
return str;
JSSTRING_SET_MUTABLE(str);
return str;
}
JS_PUBLIC_API(JSString *)
@ -5218,11 +5225,7 @@ JS_PUBLIC_API(JSBool)
JS_MakeStringImmutable(JSContext *cx, JSString *str)
{
CHECK_REQUEST(cx);
if (!js_UndependString(cx, str))
return JS_FALSE;
*js_GetGCThingFlags(str) &= ~GCF_MUTABLE;
return JS_TRUE;
return js_MakeStringImmutable(cx, str);
}
JS_PUBLIC_API(JSBool)

View File

@ -602,8 +602,7 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op,
MAKE_BUSY(he);
if (sep) {
sepstr = JSSTRING_CHARS(sep);
seplen = JSSTRING_LENGTH(sep);
JSSTRING_CHARS_AND_LENGTH(sep, sepstr, seplen);
} else {
sepstr = NULL; /* indicates to use "," as separator */
seplen = 1;
@ -715,7 +714,7 @@ array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op,
}
chars[nchars] = 0;
JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth);
str = js_NewString(cx, chars, nchars, 0);
str = js_NewString(cx, chars, nchars);
if (!str) {
free(chars);
return JS_FALSE;

View File

@ -632,14 +632,14 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
if (flags & ATOM_TMPSTR) {
if (flags & ATOM_NOCOPY) {
key = js_NewString(cx, str->chars, str->length, 0);
key = js_NewString(cx, str->u.chars, str->length);
if (!key)
return NULL;
/* Transfer ownership of str->chars to GC-controlled string. */
str->chars = NULL;
str->u.chars = NULL;
} else {
key = js_NewStringCopyN(cx, str->chars, str->length);
key = js_NewStringCopyN(cx, str->u.chars, str->length);
if (!key)
return NULL;
}
@ -708,10 +708,9 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
flags |= ATOM_NOCOPY;
}
str.chars = chars;
str.length = inflatedLength;
JSSTRING_INIT(&str, (jschar *)chars, inflatedLength);
atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
if (chars != inflated && str.chars)
if (chars != inflated && str.u.chars)
JS_free(cx, chars);
return atom;
}
@ -721,8 +720,7 @@ js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags)
{
JSString str;
str.chars = (jschar *)chars;
str.length = length;
JSSTRING_INIT(&str, (jschar *)chars, length);
return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
}
@ -733,8 +731,7 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
JSAtomState *state;
JSDHashEntryHdr *hdr;
str.chars = (jschar *)chars;
str.length = length;
JSSTRING_INIT(&str, (jschar *)chars, length);
state = &cx->runtime->atomState;
JS_LOCK(&state->lock, cx);

View File

@ -622,8 +622,8 @@ date_parseString(JSString *str, jsdouble *result)
{
jsdouble msec;
const jschar *s = JSSTRING_CHARS(str);
size_t limit = JSSTRING_LENGTH(str);
const jschar *s;
size_t limit;
size_t i = 0;
int year = -1;
int mon = -1;
@ -639,6 +639,7 @@ date_parseString(JSString *str, jsdouble *result)
int temp;
JSBool seenmonthname = JS_FALSE;
JSSTRING_CHARS_AND_LENGTH(str, s, limit);
if (limit == 0)
goto syntax;
while (i < limit) {

View File

@ -636,7 +636,10 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv)
#define APPEND_STRING_TO_STACK(str) \
JS_BEGIN_MACRO \
JSString *str_ = str; \
size_t length_ = JSSTRING_LENGTH(str_); \
jschar *chars_; \
size_t length_; \
\
JSSTRING_CHARS_AND_LENGTH(str_, chars_, length_); \
if (length_ > stackmax - stacklen) { \
void *ptr_; \
if (stackmax >= STACK_LENGTH_LIMIT || \
@ -649,7 +652,7 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv)
goto bad; \
stackbuf = (jschar *) ptr_; \
} \
js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \
js_strncpy(stackbuf + stacklen, chars_, length_); \
stacklen += length_; \
JS_END_MACRO
@ -701,7 +704,7 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv)
}
stackbuf[stacklen] = 0;
str = js_NewString(cx, stackbuf, stacklen, 0);
str = js_NewString(cx, stackbuf, stacklen);
if (str)
return str;
@ -847,7 +850,7 @@ exn_toString(JSContext *cx, uintN argc, jsval *vp)
cp += message_length;
*cp = 0;
result = js_NewString(cx, chars, length, 0);
result = js_NewString(cx, chars, length);
if (!result) {
JS_free(cx, chars);
return JS_FALSE;
@ -973,7 +976,7 @@ exn_toSource(JSContext *cx, uintN argc, jsval *vp)
*cp++ = ')'; *cp++ = ')'; *cp = 0;
result = js_NewString(cx, chars, length, 0);
result = js_NewString(cx, chars, length);
if (!result) {
JS_free(cx, chars);
return JS_FALSE;

View File

@ -247,11 +247,11 @@ static uint8 GCTypeToTraceKindMap[GCX_NTYPES] = {
JSTRACE_OBJECT, /* GCX_OBJECT */
JSTRACE_STRING, /* GCX_STRING */
JSTRACE_DOUBLE, /* GCX_DOUBLE */
JSTRACE_STRING, /* GCX_MUTABLE_STRING */
JSTRACE_FUNCTION, /* GCX_FUNCTION */
JSTRACE_NAMESPACE, /* GCX_NAMESPACE */
JSTRACE_QNAME, /* GCX_QNAME */
JSTRACE_XML, /* GCX_XML */
(uint8)-1, /* unused */
JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 0 */
JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 1 */
JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 2 */
@ -561,11 +561,11 @@ static GCFinalizeOp gc_finalizers[GCX_NTYPES] = {
(GCFinalizeOp) js_FinalizeObject, /* GCX_OBJECT */
(GCFinalizeOp) js_FinalizeString, /* GCX_STRING */
(GCFinalizeOp) js_FinalizeDouble, /* GCX_DOUBLE */
(GCFinalizeOp) js_FinalizeString, /* GCX_MUTABLE_STRING */
(GCFinalizeOp) js_FinalizeFunction, /* GCX_FUNCTION */
(GCFinalizeOp) js_FinalizeXMLNamespace, /* GCX_NAMESPACE */
(GCFinalizeOp) js_FinalizeXMLQName, /* GCX_QNAME */
(GCFinalizeOp) js_FinalizeXML, /* GCX_XML */
NULL, /* unused */
NULL, /* GCX_EXTERNAL_STRING */
NULL,
NULL,

View File

@ -53,12 +53,10 @@ JS_BEGIN_EXTERN_C
#define GCX_OBJECT 0 /* JSObject */
#define GCX_STRING 1 /* JSString */
#define GCX_DOUBLE 2 /* jsdouble */
#define GCX_MUTABLE_STRING 3 /* JSString that's mutable --
single-threaded only! */
#define GCX_FUNCTION 4 /* JSFunction */
#define GCX_NAMESPACE 5 /* JSXMLNamespace */
#define GCX_QNAME 6 /* JSXMLQName */
#define GCX_XML 7 /* JSXML */
#define GCX_FUNCTION 3 /* JSFunction */
#define GCX_NAMESPACE 4 /* JSXMLNamespace */
#define GCX_QNAME 5 /* JSXMLQName */
#define GCX_XML 6 /* JSXML */
#define GCX_EXTERNAL_STRING 8 /* JSString w/ external chars */
#define GCX_NTYPES_LOG2 4 /* type index bits */
@ -72,13 +70,6 @@ JS_BEGIN_EXTERN_C
#define GCF_LOCKSHIFT (GCX_NTYPES_LOG2 + 3) /* lock bit shift */
#define GCF_LOCK JS_BIT(GCF_LOCKSHIFT) /* lock request bit in API */
/* Pseudo-flag that modifies GCX_STRING to make GCX_MUTABLE_STRING. */
#define GCF_MUTABLE 2
#if (GCX_STRING | GCF_MUTABLE) != GCX_MUTABLE_STRING
# error "mutable string type index botch!"
#endif
extern JS_FRIEND_API(uint8 *)
js_GetGCThingFlags(void *thing);

View File

@ -375,22 +375,6 @@ ShareScope(JSContext *cx, JSScope *scope)
* updates rt->sharedScopes.
*/
static JSBool
MakeStringImmutable(JSContext *cx, JSString *str)
{
uint8 *flagp;
flagp = js_GetGCThingFlags(str);
if (*flagp & GCF_MUTABLE) {
if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(cx, str)) {
JS_RUNTIME_METER(cx->runtime, badUndependStrings);
return JS_FALSE;
}
*flagp &= ~GCF_MUTABLE;
}
return JS_TRUE;
}
void
js_FinishSharingScope(JSContext *cx, JSScope *scope)
{
@ -403,7 +387,7 @@ js_FinishSharingScope(JSContext *cx, JSScope *scope)
for (i = 0; i != nslots; ++i) {
v = STOBJ_GET_SLOT(obj, i);
if (JSVAL_IS_STRING(v) &&
!MakeStringImmutable(cx, JSVAL_TO_STRING(v))) {
!js_MakeStringImmutable(cx, JSVAL_TO_STRING(v))) {
/*
* FIXME bug 363059: The following error recovery changes the
* execution semantic arbitrary and silently ignores any errors
@ -677,7 +661,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
/* Any string stored in a thread-safe object must be immutable. */
if (JSVAL_IS_STRING(v) &&
!MakeStringImmutable(cx, JSVAL_TO_STRING(v))) {
!js_MakeStringImmutable(cx, JSVAL_TO_STRING(v))) {
/* FIXME bug 363059: See comments in js_FinishSharingScope. */
v = JSVAL_NULL;
}

View File

@ -92,13 +92,13 @@ num_parseFloat(JSContext *cx, uintN argc, jsval *vp)
{
JSString *str;
jsdouble d;
const jschar *bp, *ep;
const jschar *bp, *end, *ep;
str = js_ValueToString(cx, vp[2]);
if (!str)
return JS_FALSE;
bp = JSSTRING_CHARS(str);
if (!js_strtod(cx, bp, bp + JSSTRING_LENGTH(str), &ep, &d))
JSSTRING_CHARS_AND_END(str, bp, end);
if (!js_strtod(cx, bp, end, &ep, &d))
return JS_FALSE;
if (ep == bp) {
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
@ -114,7 +114,7 @@ num_parseInt(JSContext *cx, uintN argc, jsval *vp)
jsint radix;
JSString *str;
jsdouble d;
const jschar *bp, *ep;
const jschar *bp, *end, *ep;
if (argc > 1) {
if (!js_ValueToECMAInt32(cx, vp[3], &radix))
@ -130,8 +130,8 @@ num_parseInt(JSContext *cx, uintN argc, jsval *vp)
str = js_ValueToString(cx, vp[2]);
if (!str)
return JS_FALSE;
bp = JSSTRING_CHARS(str);
if (!js_strtointeger(cx, bp, bp + JSSTRING_LENGTH(str), &ep, radix, &d))
JSSTRING_CHARS_AND_END(str, bp, end);
if (!js_strtointeger(cx, bp, end, &ep, radix, &d))
return JS_FALSE;
if (ep == bp) {
*vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
@ -724,8 +724,7 @@ js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
* interpreted as decimal by js_strtod and will never get passed to
* js_strtointeger (which would interpret them as octal).
*/
bp = JSSTRING_CHARS(str);
end = bp + JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_END(str, bp, end);
if ((!js_strtod(cx, bp, end, &ep, dp) ||
js_SkipWhiteSpace(ep, end) != end) &&
(!js_strtointeger(cx, bp, end, &ep, 0, dp) ||

View File

@ -937,8 +937,7 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp)
}
*vp = STRING_TO_JSVAL(idstr); /* local root */
}
idstrchars = JSSTRING_CHARS(idstr);
idstrlength = JSSTRING_LENGTH(idstr);
JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength);
for (j = 0; j < valcnt; j++) {
/* Convert val[j] to its canonical source form. */
@ -948,8 +947,7 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp)
goto error;
}
vp[2 + j] = STRING_TO_JSVAL(valstr); /* local root */
vchars = JSSTRING_CHARS(valstr);
vlength = JSSTRING_LENGTH(valstr);
JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength);
if (vchars[0] == '#')
needOldStyleGetterSetter = JS_TRUE;
@ -1131,7 +1129,7 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
}
make_string:
str = js_NewString(cx, chars, nchars, 0);
str = js_NewString(cx, chars, nchars);
if (!str) {
free(chars);
return JS_FALSE;
@ -1170,7 +1168,7 @@ obj_toString(JSContext *cx, uintN argc, jsval *vp)
chars[nchars++] = ']';
chars[nchars] = 0;
str = js_NewString(cx, chars, nchars, 0);
str = js_NewString(cx, chars, nchars);
if (!str) {
JS_free(cx, chars);
return JS_FALSE;
@ -2844,7 +2842,7 @@ 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_->chars; \
const jschar *cp_ = str_->u.chars; \
JSBool negative_ = (*cp_ == '-'); \
if (negative_) cp_++; \
if (JS7_ISDEC(*cp_)) { \
@ -4965,8 +4963,8 @@ void printString(JSString *str) {
size_t i, n;
jschar *s;
fprintf(stderr, "string (%p) \"", (void *)str);
s = JSSTRING_CHARS(str);
for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
JSSTRING_CHARS_AND_LENGTH(str, s, n);
for (i=0; i < n; i++)
fputc(s[i], stderr);
fputc('"', stderr);
fputc('\n', stderr);

View File

@ -467,8 +467,7 @@ SprintString(Sprinter *sp, JSString *str)
size_t length, size;
ptrdiff_t offset;
chars = JSSTRING_CHARS(str);
length = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, chars, length);
if (length == 0)
return sp->offset;
@ -537,8 +536,7 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote)
return NULL;
/* Loop control variables: z points at end of string sentinel. */
s = JSSTRING_CHARS(str);
z = s + JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_END(str, s, z);
for (t = s; t < z; s = ++t) {
/* Move t forward from s past un-quote-worthy characters. */
c = *t;

View File

@ -6653,7 +6653,7 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
if (!chars)
return JS_FALSE;
str = js_NewString(cx, chars, length, 0);
str = js_NewString(cx, chars, length);
if (!str) {
JS_free(cx, chars);
return JS_FALSE;
@ -6663,7 +6663,7 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
str2 = ATOM_TO_STRING(pn2->pn_atom);
length2 = str2->length;
js_strncpy(chars, str2->chars, length2);
js_strncpy(chars, str2->u.chars, length2);
chars += length2;
}
*chars = 0;

View File

@ -97,7 +97,6 @@ typedef uint32 jsatomid;
/* Struct typedefs. */
typedef struct JSArgumentFormatMap JSArgumentFormatMap;
typedef struct JSCodeGenerator JSCodeGenerator;
typedef struct JSDependentString JSDependentString;
typedef struct JSGCThing JSGCThing;
typedef struct JSGenerator JSGenerator;
typedef struct JSParseContext JSParseContext;

View File

@ -2032,8 +2032,8 @@ js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts,
flags = 0;
if (opt) {
s = JSSTRING_CHARS(opt);
for (i = 0, n = JSSTRING_LENGTH(opt); i < n; i++) {
JSSTRING_CHARS_AND_LENGTH(opt, s, n);
for (i = 0; i < n; i++) {
switch (s[i]) {
case 'g':
flags |= JSREG_GLOB;
@ -3373,10 +3373,9 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
* and we never let cp get beyond cpend.
*/
start = *indexp;
length = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, cp, length);
if (start > length)
start = length;
cp = JSSTRING_CHARS(str);
gData.cpbegin = cp;
gData.cpend = cp + length;
cp += start;
@ -3914,8 +3913,7 @@ js_regexp_toString(JSContext *cx, JSObject *obj, jsval *vp)
return JS_TRUE;
}
source = JSSTRING_CHARS(re->source);
length = JSSTRING_LENGTH(re->source);
JSSTRING_CHARS_AND_LENGTH(re->source, source, length);
if (length == 0) {
source = empty_regexp_ucstr;
length = sizeof(empty_regexp_ucstr) / sizeof(jschar) - 1;
@ -3946,7 +3944,7 @@ js_regexp_toString(JSContext *cx, JSObject *obj, jsval *vp)
JS_UNLOCK_OBJ(cx, obj);
chars[length] = 0;
str = js_NewString(cx, chars, length, 0);
str = js_NewString(cx, chars, length);
if (!str) {
JS_free(cx, chars);
return JS_FALSE;
@ -4021,8 +4019,7 @@ regexp_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
}
/* Escape any naked slashes in the regexp source. */
length = JSSTRING_LENGTH(str);
start = JSSTRING_CHARS(str);
JSSTRING_CHARS_AND_LENGTH(str, start, length);
end = start + length;
nstart = ncp = NULL;
for (cp = start; cp < end; cp++) {
@ -4053,7 +4050,7 @@ regexp_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
/* Don't forget to store the backstop after the new string. */
JS_ASSERT((size_t)(ncp - nstart) == length);
*ncp = 0;
str = js_NewString(cx, nstart, length, 0);
str = js_NewString(cx, nstart, length);
if (!str) {
JS_free(cx, nstart);
return JS_FALSE;

View File

@ -157,10 +157,9 @@ js_IsIdentifier(JSString *str)
size_t length;
jschar c, *chars, *end;
length = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, chars, length);
if (length == 0)
return JS_FALSE;
chars = JSSTRING_CHARS(str);
c = *chars;
if (!JS_ISIDSTART(c))
return JS_FALSE;

View File

@ -129,8 +129,7 @@ script_toSource(JSContext *cx, uintN argc, jsval *vp)
str = js_QuoteString(cx, str, '\'');
if (!str)
return JS_FALSE;
s = JSSTRING_CHARS(str);
k = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, s, k);
n += k;
}
@ -725,8 +724,7 @@ script_thaw(JSContext *cx, uintN argc, jsval *vp)
if (!xdr)
return JS_FALSE;
buf = JSSTRING_CHARS(str);
len = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, buf, len);
#if IS_BIG_ENDIAN
{
jschar *from, *to;

View File

@ -91,12 +91,11 @@ js_MinimizeDependentStrings(JSString *str, int level, JSString **basep)
} while (JSSTRING_IS_DEPENDENT(base));
}
if (start == 0) {
JS_ASSERT(JSSTRING_IS_PREFIX(str));
JS_ASSERT(JSSTRDEP_IS_PREFIX(str));
JSPREFIX_SET_BASE(str, base);
} else if (start <= JSSTRDEP_START_MASK) {
length = JSSTRDEP_LENGTH(str);
JSSTRDEP_SET_START_AND_LENGTH(str, start, length);
JSSTRDEP_SET_BASE(str, base);
JSSTRDEP_INIT(str, base, start, length);
}
}
*basep = base;
@ -111,18 +110,17 @@ js_GetDependentStringChars(JSString *str)
start = js_MinimizeDependentStrings(str, 0, &base);
JS_ASSERT(!JSSTRING_IS_DEPENDENT(base));
JS_ASSERT(start < base->length);
return base->chars + start;
JS_ASSERT(start < (base->length & ~JSSTRFLAG_MUTABLE));
return base->u.chars + start;
}
const jschar *
js_GetStringChars(JSContext *cx, JSString *str)
{
if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(cx, str))
if (!js_MakeStringImmutable(cx, str))
return NULL;
*js_GetGCThingFlags(str) &= ~GCF_MUTABLE;
return str->chars;
JS_ASSERT(!JSSTRING_IS_DEPENDENT(str));
return str->u.chars;
}
JSString *
@ -130,26 +128,19 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
{
size_t rn, ln, lrdist, n;
jschar *rs, *ls, *s;
JSDependentString *ldep; /* non-null if left should become dependent */
JSString *ldep; /* non-null if left should become dependent */
JSString *str;
if (JSSTRING_IS_DEPENDENT(right)) {
rn = JSSTRDEP_LENGTH(right);
rs = JSSTRDEP_CHARS(right);
} else {
rn = right->length;
rs = right->chars;
}
JSSTRING_CHARS_AND_LENGTH(right, rs, rn);
if (rn == 0)
return left;
if (JSSTRING_IS_DEPENDENT(left) ||
!(*js_GetGCThingFlags(left) & GCF_MUTABLE)) {
JSSTRING_CHARS_AND_LENGTH(left, ls, ln);
if (ln == 0)
return right;
if (!JSSTRING_IS_MUTABLE(left)) {
/* We must copy if left does not own a buffer to realloc. */
ln = JSSTRING_LENGTH(left);
if (ln == 0)
return right;
ls = JSSTRING_CHARS(left);
s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar));
if (!s)
return NULL;
@ -157,10 +148,7 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
ldep = NULL;
} else {
/* We can realloc left's space and make it depend on our result. */
ln = left->length;
if (ln == 0)
return right;
ls = left->chars;
JS_ASSERT(!JSSTRING_IS_DEPENDENT(left));
s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar));
if (!s)
return NULL;
@ -169,14 +157,14 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
lrdist = (size_t)(rs - ls);
if (lrdist < ln)
rs = s + lrdist;
left->chars = ls = s;
ldep = JSSTRDEP(left);
left->u.chars = ls = s;
ldep = left;
}
js_strncpy(s + ln, rs, rn);
n = ln + rn;
s[n] = 0;
str = js_NewString(cx, s, n, GCF_MUTABLE);
str = js_NewString(cx, s, n);
if (!str) {
/* Out of memory: clean up any space we (re-)allocated. */
if (!ldep) {
@ -184,13 +172,14 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
} else {
s = (jschar *) JS_realloc(cx, ls, (ln + 1) * sizeof(jschar));
if (s)
left->chars = s;
left->u.chars = s;
}
} else {
JSSTRING_SET_MUTABLE(str);
/* Morph left into a dependent prefix if we realloc'd its buffer. */
if (ldep) {
JSPREFIX_SET_LENGTH(ldep, ln);
JSPREFIX_SET_BASE(ldep, str);
JSPREFIX_INIT(ldep, str, ln);
#ifdef DEBUG
{
JSRuntime *rt = cx->runtime;
@ -222,8 +211,7 @@ js_UndependString(JSContext *cx, JSString *str)
js_strncpy(s, JSSTRDEP_CHARS(str), n);
s[n] = 0;
str->length = n;
str->chars = s;
JSSTRING_INIT(str, s, n);
#ifdef DEBUG
{
@ -237,7 +225,18 @@ js_UndependString(JSContext *cx, JSString *str)
#endif
}
return str->chars;
return str->u.chars;
}
JSBool
js_MakeStringImmutable(JSContext *cx, JSString *str)
{
if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(cx, str)) {
JS_RUNTIME_METER(cx->runtime, badUndependStrings);
return JS_FALSE;
}
JSSTRING_CLEAR_MUTABLE(str);
return JS_TRUE;
}
/*
@ -332,8 +331,8 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval
return JS_FALSE;
argv[0] = STRING_TO_JSVAL(str);
chars = JSSTRING_CHARS(str);
length = newlength = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, chars, length);
newlength = length;
/* Take a first pass and see how big the result string will need to be. */
for (i = 0; i < length; i++) {
@ -388,7 +387,7 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval
JS_ASSERT(ni == newlength);
newchars[newlength] = 0;
str = js_NewString(cx, newchars, newlength, 0);
str = js_NewString(cx, newchars, newlength);
if (!str) {
JS_free(cx, newchars);
return JS_FALSE;
@ -419,8 +418,7 @@ str_unescape(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
vp[2] = STRING_TO_JSVAL(str);
chars = JSSTRING_CHARS(str);
length = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, chars, length);
/* Don't bother allocating less space for the new string. */
newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
@ -450,7 +448,7 @@ str_unescape(JSContext *cx, uintN argc, jsval *vp)
}
newchars[ni] = 0;
str = js_NewString(cx, newchars, ni, 0);
str = js_NewString(cx, newchars, ni);
if (!str) {
JS_free(cx, newchars);
return JS_FALSE;
@ -644,8 +642,7 @@ str_toSource(JSContext *cx, uintN argc, jsval *vp)
if (!str)
return JS_FALSE;
j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name);
s = JSSTRING_CHARS(str);
k = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, s, k);
n = j + k + 2;
t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
if (!t)
@ -657,7 +654,7 @@ str_toSource(JSContext *cx, uintN argc, jsval *vp)
t[i++] = ')';
t[i++] = ')';
t[i] = 0;
str = js_NewString(cx, t, n, 0);
str = js_NewString(cx, t, n);
if (!str) {
JS_free(cx, t);
return JS_FALSE;
@ -738,15 +735,14 @@ str_toLowerCase(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
vp[1] = STRING_TO_JSVAL(str);
n = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, s, n);
news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
if (!news)
return JS_FALSE;
s = JSSTRING_CHARS(str);
for (i = 0; i < n; i++)
news[i] = JS_TOLOWER(s[i]);
news[n] = 0;
str = js_NewString(cx, news, n, 0);
str = js_NewString(cx, news, n);
if (!str) {
JS_free(cx, news);
return JS_FALSE;
@ -786,15 +782,14 @@ str_toUpperCase(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
vp[1] = STRING_TO_JSVAL(str);
n = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, s, n);
news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
if (!news)
return JS_FALSE;
s = JSSTRING_CHARS(str);
for (i = 0; i < n; i++)
news[i] = JS_TOUPPER(s[i]);
news[n] = 0;
str = js_NewString(cx, news, n, 0);
str = js_NewString(cx, news, n);
if (!str) {
JS_free(cx, news);
return JS_FALSE;
@ -1673,7 +1668,7 @@ str_replace(JSContext *cx, uintN argc, jsval *vp)
rightlen);
chars[length] = 0;
str = js_NewString(cx, chars, length, 0);
str = js_NewString(cx, chars, length);
if (!str) {
JS_free(cx, chars);
ok = JS_FALSE;
@ -1844,8 +1839,7 @@ str_split(JSContext *cx, uintN argc, jsval *vp)
* Point sep at a local copy of str2's header because find_split
* will modify sep->length.
*/
tmp.length = JSSTRING_LENGTH(str2);
tmp.chars = JSSTRING_CHARS(str2);
JSSTRING_CHARS_AND_LENGTH(str2, tmp.chars, tmp.length);
sep = &tmp;
re = NULL;
}
@ -2134,7 +2128,7 @@ tagify(JSContext *cx, const char *begin, JSString *param, const char *end,
JS_ASSERT(j == taglen);
tagbuf[j] = 0;
str = js_NewString(cx, tagbuf, taglen, 0);
str = js_NewString(cx, tagbuf, taglen);
if (!str) {
free((char *)tagbuf);
return JS_FALSE;
@ -2334,7 +2328,7 @@ str_fromCharCode(JSContext *cx, uintN argc, jsval *vp)
chars[i] = (jschar)code;
}
chars[i] = 0;
str = js_NewString(cx, chars, argc, 0);
str = js_NewString(cx, chars, argc);
if (!str) {
JS_free(cx, chars);
return JS_FALSE;
@ -2409,8 +2403,8 @@ js_GetUnitString(JSContext *cx, jschar c)
}
if (!rt->unitStrings[c]) {
cp = UNIT_STRING_SPACE_RT(rt);
str = js_NewString(cx, cp + 2 * c, 1, GCF_LOCK);
if (!str)
str = js_NewString(cx, cp + 2 * c, 1);
if (!str || !js_LockGCThing(cx, str))
return NULL;
JS_LOCK_GC(rt);
if (!rt->unitStrings[c]) {
@ -2479,7 +2473,7 @@ js_InitStringClass(JSContext *cx, JSObject *obj)
}
JSString *
js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag)
js_NewString(JSContext *cx, jschar *chars, size_t length)
{
JSString *str;
@ -2488,11 +2482,10 @@ js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag)
return NULL;
}
str = (JSString *) js_NewGCThing(cx, gcflag | GCX_STRING, sizeof(JSString));
str = (JSString *) js_NewGCThing(cx, GCX_STRING, sizeof(JSString));
if (!str)
return NULL;
str->length = length;
str->chars = chars;
JSSTRING_INIT(str, chars, length);
#ifdef DEBUG
{
JSRuntime *rt = cx->runtime;
@ -2510,7 +2503,7 @@ JSString *
js_NewDependentString(JSContext *cx, JSString *base, size_t start,
size_t length)
{
JSDependentString *ds;
JSString *ds;
if (length == 0)
return cx->runtime->emptyString;
@ -2523,17 +2516,13 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start,
return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length);
}
ds = (JSDependentString *)
js_NewGCThing(cx, GCX_MUTABLE_STRING, sizeof(JSString));
ds = (JSString *)js_NewGCThing(cx, GCX_STRING, sizeof(JSString));
if (!ds)
return NULL;
if (start == 0) {
JSPREFIX_SET_LENGTH(ds, length);
JSPREFIX_SET_BASE(ds, base);
} else {
JSSTRDEP_SET_START_AND_LENGTH(ds, start, length);
JSSTRDEP_SET_BASE(ds, base);
}
if (start == 0)
JSPREFIX_INIT(ds, base, length);
else
JSSTRDEP_INIT(ds, base, start, length);
#ifdef DEBUG
{
JSRuntime *rt = cx->runtime;
@ -2549,7 +2538,7 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start,
rt->lengthSquaredSum += (double)length * (double)length));
}
#endif
return (JSString *)ds;
return ds;
}
#ifdef DEBUG
@ -2602,7 +2591,7 @@ js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
return NULL;
js_strncpy(news, s, n);
news[n] = 0;
str = js_NewString(cx, news, n, 0);
str = js_NewString(cx, news, n);
if (!str)
JS_free(cx, news);
return str;
@ -2621,7 +2610,7 @@ js_NewStringCopyZ(JSContext *cx, const jschar *s)
if (!news)
return NULL;
memcpy(news, s, m);
str = js_NewString(cx, news, n, 0);
str = js_NewString(cx, news, n);
if (!str)
JS_free(cx, news);
return str;
@ -2675,15 +2664,15 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str)
valid = JS_TRUE;
} else {
/* A stillborn string has null chars, so is not valid. */
valid = (str->chars != NULL);
if (valid && !IN_UNIT_STRING_SPACE_RT(rt, str->chars))
free(str->chars);
valid = (str->u.chars != NULL);
if (valid && !IN_UNIT_STRING_SPACE_RT(rt, str->u.chars))
free(str->u.chars);
}
if (valid) {
if (valid)
js_PurgeDeflatedStringCache(rt, str);
str->chars = NULL;
}
str->length = 0;
#ifdef DEBUG
memset(str, JS_FREE_PATTERN, sizeof *str);
#endif
}
JS_FRIEND_API(const char *)
@ -2766,12 +2755,12 @@ js_ValueToSource(JSContext *cx, jsval v)
uint32
js_HashString(JSString *str)
{
uint32 h;
const jschar *s;
size_t n;
uint32 h;
h = 0;
for (s = JSSTRING_CHARS(str), n = JSSTRING_LENGTH(str); n; s++, n--)
JSSTRING_CHARS_AND_LENGTH(str, s, n);
for (h = 0; n; s++, n--)
h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s;
return h;
}
@ -2823,8 +2812,8 @@ js_CompareStrings(JSString *str1, JSString *str2)
if (str1 == str2)
return 0;
l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2);
s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2);
JSSTRING_CHARS_AND_LENGTH(str1, s1, l1);
JSSTRING_CHARS_AND_LENGTH(str2, s2, l2);
n = JS_MIN(l1, l2);
for (i = 0; i < n; i++) {
cmp = s1[i] - s2[i];
@ -4608,27 +4597,55 @@ const jschar js_uriUnescaped_ucstr[] =
#define URI_CHUNK 64U
/* Concatenate jschars onto an unshared/newborn JSString. */
/* Concatenate jschars onto the buffer */
static JSBool
AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length)
AddCharsToURI(JSContext *cx, JSCharBuffer *buf,
const jschar *chars, size_t length)
{
size_t total;
jschar *newchars;
JS_ASSERT(!JSSTRING_IS_DEPENDENT(str));
total = str->length + length + 1;
if (!str->chars ||
JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(str->length + 1, URI_CHUNK)) {
total = buf->length + length + 1;
if (!buf->chars ||
JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(buf->length + 1, URI_CHUNK)) {
total = JS_ROUNDUP(total, URI_CHUNK);
newchars = (jschar *) JS_realloc(cx, str->chars,
newchars = (jschar *) JS_realloc(cx, buf->chars,
total * sizeof(jschar));
if (!newchars)
return JS_FALSE;
str->chars = newchars;
buf->chars = newchars;
}
js_strncpy(str->chars + str->length, chars, length);
str->length += length;
str->chars[str->length] = 0;
js_strncpy(buf->chars + buf->length, chars, length);
buf->length += length;
buf->chars[buf->length] = 0;
return JS_TRUE;
}
static JSBool
TransferBufferToString(JSContext *cx, JSCharBuffer *cb, jsval *rval)
{
jschar *chars;
size_t n;
JSString *str;
/*
* Shrinking realloc can fail (e.g., with a BSD-style allocator), but we
* don't worry about that case here.
*/
n = cb->length;
chars = (jschar *) JS_realloc(cx, cb->chars, (n + 1) * sizeof(jschar));
if (!chars)
chars = cb->chars;
str = js_NewString(cx, chars, n);
if (!str)
return JS_FALSE;
/* Successful allocation transfer ownership of cb->chars to the string. */
#ifdef DEBUG
memset(cb, JS_FREE_PATTERN, sizeof *cb);
#endif
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
@ -4644,37 +4661,36 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
const jschar *unescapedSet2, jsval *rval)
{
size_t length, j, k, L;
JSCharBuffer cb;
jschar *chars, c, c2;
uint32 v;
uint8 utf8buf[6];
jschar hexBuf[4];
static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */
JSString *R;
length = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, chars, length);
if (length == 0) {
*rval = STRING_TO_JSVAL(cx->runtime->emptyString);
return JS_TRUE;
}
R = js_NewString(cx, NULL, 0, 0);
if (!R)
return JS_FALSE;
cb.length = 0;
cb.chars = NULL;
/* From this point the control must goto bad on failures. */
hexBuf[0] = '%';
hexBuf[3] = 0;
chars = JSSTRING_CHARS(str);
for (k = 0; k < length; k++) {
c = chars[k];
if (js_strchr(unescapedSet, c) ||
(unescapedSet2 && js_strchr(unescapedSet2, c))) {
if (!AddCharsToURI(cx, R, &c, 1))
return JS_FALSE;
if (!AddCharsToURI(cx, &cb, &c, 1))
goto bad;
} else {
if ((c >= 0xDC00) && (c <= 0xDFFF)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_URI, NULL);
return JS_FALSE;
goto bad;
}
if (c < 0xD800 || c > 0xDBFF) {
v = c;
@ -4683,13 +4699,13 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
if (k == length) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_URI, NULL);
return JS_FALSE;
goto bad;
}
c2 = chars[k];
if ((c2 < 0xDC00) || (c2 > 0xDFFF)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_URI, NULL);
return JS_FALSE;
goto bad;
}
v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000;
}
@ -4697,54 +4713,51 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,
for (j = 0; j < L; j++) {
hexBuf[1] = HexDigits[utf8buf[j] >> 4];
hexBuf[2] = HexDigits[utf8buf[j] & 0xf];
if (!AddCharsToURI(cx, R, hexBuf, 3))
return JS_FALSE;
if (!AddCharsToURI(cx, &cb, hexBuf, 3))
goto bad;
}
}
}
/*
* Shrinking realloc can fail (e.g., with a BSD-style allocator), but we
* don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1
* more jschars than it needs.
*/
chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar));
if (chars)
R->chars = chars;
*rval = STRING_TO_JSVAL(R);
if (!TransferBufferToString(cx, &cb, rval))
goto bad;
return JS_TRUE;
bad:
JS_free(cx, cb.chars);
return JS_FALSE;
}
static JSBool
Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)
{
size_t length, start, k;
JSCharBuffer cb;
jschar *chars, c, H;
uint32 v;
jsuint B;
uint8 octets[6];
JSString *R;
intN j, n;
length = JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_LENGTH(str, chars, length);
if (length == 0) {
*rval = STRING_TO_JSVAL(cx->runtime->emptyString);
return JS_TRUE;
}
R = js_NewString(cx, NULL, 0, 0);
if (!R)
return JS_FALSE;
cb.length = 0;
cb.chars = NULL;
chars = JSSTRING_CHARS(str);
/* From this point the control must goto bad on failures. */
for (k = 0; k < length; k++) {
c = chars[k];
if (c == '%') {
start = k;
if ((k + 2) >= length)
goto bad;
goto report_bad_uri;
if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
goto bad;
goto report_bad_uri;
B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
k += 2;
if (!(B & 0x80)) {
@ -4754,19 +4767,19 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)
while (B & (0x80 >> n))
n++;
if (n == 1 || n > 6)
goto bad;
goto report_bad_uri;
octets[0] = (uint8)B;
if (k + 3 * (n - 1) >= length)
goto bad;
goto report_bad_uri;
for (j = 1; j < n; j++) {
k++;
if (chars[k] != '%')
goto bad;
goto report_bad_uri;
if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))
goto bad;
goto report_bad_uri;
B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);
if ((B & 0xC0) != 0x80)
goto bad;
goto report_bad_uri;
k += 2;
octets[j] = (char)B;
}
@ -4774,41 +4787,39 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)
if (v >= 0x10000) {
v -= 0x10000;
if (v > 0xFFFFF)
goto bad;
goto report_bad_uri;
c = (jschar)((v & 0x3FF) + 0xDC00);
H = (jschar)((v >> 10) + 0xD800);
if (!AddCharsToURI(cx, R, &H, 1))
return JS_FALSE;
if (!AddCharsToURI(cx, &cb, &H, 1))
goto bad;
} else {
c = (jschar)v;
}
}
if (js_strchr(reservedSet, c)) {
if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1)))
return JS_FALSE;
if (!AddCharsToURI(cx, &cb, &chars[start], (k - start + 1)))
goto bad;
} else {
if (!AddCharsToURI(cx, R, &c, 1))
return JS_FALSE;
if (!AddCharsToURI(cx, &cb, &c, 1))
goto bad;
}
} else {
if (!AddCharsToURI(cx, R, &c, 1))
if (!AddCharsToURI(cx, &cb, &c, 1))
return JS_FALSE;
}
}
/*
* Shrinking realloc can fail (e.g., with a BSD-style allocator), but we
* don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1
* more jschars than it needs.
*/
chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar));
if (chars)
R->chars = chars;
*rval = STRING_TO_JSVAL(R);
if (!TransferBufferToString(cx, &cb, rval))
goto bad;
return JS_TRUE;
bad:
report_bad_uri:
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);
/* FALL THROUGH */
bad:
JS_free(cx, cb.chars);
return JS_FALSE;
}
@ -4949,8 +4960,7 @@ js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,
JS_ASSERT_IF(!buffer, bufferSize == 0);
JS_ASSERT_IF(fp, !buffer);
chars = JSSTRING_CHARS(str);
charsEnd = chars + JSSTRING_LENGTH(str);
JSSTRING_CHARS_AND_END(str, chars, charsEnd);
n = 0;
--bufferSize;
state = FIRST_QUOTE;

View File

@ -55,90 +55,140 @@
JS_BEGIN_EXTERN_C
/*
* The original GC-thing "string" type, a flat character string owned by its
* GC-thing descriptor. The chars member points to a vector having byte size
* (length + 1) * sizeof(jschar), terminated at index length by a zero jschar.
* The terminator is purely a backstop, in case the chars pointer flows out to
* native code that requires \u0000 termination.
* The GC-thing "string" type.
*
* NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros,
* unless you guard str->member uses with !JSSTRING_IS_DEPENDENT(str).
* When the JSSTRFLAG_DEPENDENT bit of the length field is unset, the u.chars
* field points to a flat character array owned by its GC-thing descriptor.
* The array is terminated at index length by a zero character and the size of
* the array in bytes is (length + 1) * sizeof(jschar). The terminator is
* purely a backstop, in case the chars pointer flows out to native code that
* requires \u0000 termination.
*
* A flat string with JSSTRFLAG_MUTABLE set means the string is accessible
* only from one thread and it is possible to turn it into a dependent string
* of the same length to optimize js_ConcatStrings. It also possible to grow
* such string but extreme care must be taken to ensure that no other code
* relies on the original length of the 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
* yet.
*
* JSSTRFLAG_PREFIX determines the kind of the dependent string. When the flag
* is unset, the length field encodes both starting position relative to the
* base string and the number of characters in the dependent string, see
* JSSTRDEP_START_MASK and JSSTRDEP_LENGTH_MASK macros below for details.
*
* When JSSTRFLAG_PREFIX is set, the dependent string is a prefix of the base
* string. The number of characters in the prefix is encoded using all
* non-flag bits of the length field and spans the same 0 .. SIZE_T_MAX/4
* range as the length of the flat string.
*
* NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros.
*/
struct JSString {
size_t length;
jschar *chars;
union {
jschar *chars;
JSString *base;
} u;
};
/*
* Overlay structure for a string that depends on another string's characters.
* Distinguished by the JSSTRFLAG_DEPENDENT bit being set in length. The base
* member may point to another dependent string if JSSTRING_CHARS has not been
* called yet. The length chars in a dependent string are stored starting at
* base->chars + start, and are not necessarily zero-terminated. If start is
* 0, it is not stored, length is a full size_t (minus the JSSTRFLAG_* bits in
* the high two positions), and the JSSTRFLAG_PREFIX flag is set.
* 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.
*/
struct JSDependentString {
size_t length;
JSString *base;
};
/* Definitions for flags stored in the high order bits of JSString.length. */
#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)
/* 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_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX)
#define JSSTRING_IS_MUTABLE(str) (((str)->length & JSSTRFLAG_MASK) == \
JSSTRFLAG_MUTABLE)
#define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \
? JSSTRDEP_CHARS(str) \
: (str)->chars)
: (str)->u.chars)
#define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \
? JSSTRDEP_LENGTH(str) \
: (str)->length)
: ((str)->length & ~JSSTRFLAG_MUTABLE))
#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)))
#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)
/* Specific JSDependentString shift/mask accessor and mutator macros. */
#define JSSTRING_INIT(str, chars_, length_) \
((void)(JS_ASSERT(((length_) & JSSTRFLAG_MASK) == 0), \
(str)->length = (length_), (str)->u.chars = (chars_)))
/* Specific mutable string manipulation macros. */
#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))
/* Specific dependent string shift/mask accessor and mutator macros. */
#define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS)
#define JSSTRDEP_START_SHIFT JSSTRDEP_LENGTH_BITS
#define JSSTRDEP_START_MASK JSSTRING_BITMASK(JSSTRDEP_START_BITS)
#define JSSTRDEP_LENGTH_BITS (JSSTRING_LENGTH_BITS / 2)
#define JSSTRDEP_LENGTH_MASK JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS)
#define JSSTRDEP(str) ((JSDependentString *)(str))
#define JSSTRDEP_START(str) (JSSTRING_IS_PREFIX(str) ? 0 \
: ((JSSTRDEP(str)->length \
#define JSSTRDEP_IS_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX)
#define JSSTRDEP_START(str) (JSSTRDEP_IS_PREFIX(str) ? 0 \
: (((str)->length \
>> JSSTRDEP_START_SHIFT) \
& JSSTRDEP_START_MASK))
#define JSSTRDEP_LENGTH(str) (JSSTRDEP(str)->length \
& (JSSTRING_IS_PREFIX(str) \
#define JSSTRDEP_LENGTH(str) ((str)->length \
& (JSSTRDEP_IS_PREFIX(str) \
? JSSTRING_LENGTH_MASK \
: JSSTRDEP_LENGTH_MASK))
#define JSSTRDEP_SET_START_AND_LENGTH(str,off,len) \
(JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT \
| ((off) << JSSTRDEP_START_SHIFT) \
| (len))
#define JSPREFIX_SET_LENGTH(str,len) \
(JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len))
#define JSSTRDEP_INIT(str,bstr,off,len) \
((str)->length = JSSTRFLAG_DEPENDENT \
| ((off) << JSSTRDEP_START_SHIFT) \
| (len), \
(str)->u.base = (bstr))
#define JSSTRDEP_BASE(str) (JSSTRDEP(str)->base)
#define JSSTRDEP_SET_BASE(str,bstr) (JSSTRDEP(str)->base = (bstr))
#define JSPREFIX_INIT(str,bstr,len) \
((str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len), \
(str)->u.base = (bstr))
#define JSSTRDEP_BASE(str) ((str)->u.base)
#define JSPREFIX_BASE(str) JSSTRDEP_BASE(str)
#define JSPREFIX_SET_BASE(str,bstr) JSSTRDEP_SET_BASE(str,bstr)
#define JSPREFIX_SET_BASE(str,bstr) ((str)->u.base = (bstr))
#define JSSTRDEP_CHARS(str) \
(JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \
? js_GetDependentStringChars(str) \
: JSSTRDEP_BASE(str)->chars + JSSTRDEP_START(str))
: JSSTRDEP_BASE(str)->u.chars + JSSTRDEP_START(str))
extern size_t
js_MinimizeDependentStrings(JSString *str, int level, JSString **basep);
@ -155,6 +205,14 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right);
extern const jschar *
js_UndependString(JSContext *cx, JSString *str);
extern JSBool
js_MakeStringImmutable(JSContext *cx, JSString *str);
typedef struct JSCharBuffer {
size_t length;
jschar *chars;
} JSCharBuffer;
struct JSSubString {
size_t length;
const jschar *chars;
@ -334,7 +392,7 @@ extern const char js_encodeURIComponent_str[];
/* GC-allocate a string descriptor for the given malloc-allocated chars. */
extern JSString *
js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag);
js_NewString(JSContext *cx, jschar *chars, size_t length);
extern JSString *
js_NewDependentString(JSContext *cx, JSString *base, size_t start,

View File

@ -534,7 +534,7 @@ qname_toString(JSContext *cx, uintN argc, jsval *vp)
*chars = '@';
js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
chars[++length] = 0;
str = js_NewString(cx, chars, length, 0);
str = js_NewString(cx, chars, length);
if (!str) {
JS_free(cx, chars);
return JS_FALSE;
@ -1402,8 +1402,7 @@ ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
JS_ASSERT(pn->pn_arity == PN_NULLARY);
str = ATOM_TO_STRING(pn->pn_atom);
length = JSSTRING_LENGTH(str);
start = JSSTRING_CHARS(str);
JSSTRING_CHARS_AND_LENGTH(str, start, length);
JS_ASSERT(length != 0 && *start != '@');
JS_ASSERT(length != 1 || *start != '*');
@ -1489,8 +1488,8 @@ ChompXMLWhitespace(JSContext *cx, JSString *str)
const jschar *cp, *start, *end;
jschar c;
length = JSSTRING_LENGTH(str);
for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
JSSTRING_CHARS_AND_LENGTH(str, start, length);
for (cp = start, end = cp + length; cp < end; cp++) {
c = *cp;
if (!JS_ISXMLSPACE(c))
break;
@ -1676,8 +1675,7 @@ ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes,
if (pn2->pn_type != TOK_XMLATTR)
goto syntax;
length = JSSTRING_LENGTH(str);
chars = JSSTRING_CHARS(str);
JSSTRING_CHARS_AND_LENGTH(str, chars, length);
if (length >= 5 &&
IS_XMLNS_CHARS(chars) &&
(length == 5 || chars[5] == ':')) {
@ -2289,7 +2287,7 @@ MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
js_strncpy(bp, suffix, suffixlength);
bp[suffixlength] = 0;
str = js_NewString(cx, base, newlength, 0);
str = js_NewString(cx, base, newlength);
if (!str)
free(base);
return str;
@ -2364,8 +2362,9 @@ EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
const jschar *cp, *start, *end;
jschar c;
length = newlength = JSSTRING_LENGTH(str);
for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
JSSTRING_CHARS_AND_LENGTH(str, start, length);
newlength = length;
for (cp = start, end = cp + length; cp < end; cp++) {
c = *cp;
if (c == '<' || c == '>')
newlength += 3;
@ -2399,7 +2398,7 @@ EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
js_AppendChar(sb, c);
}
JS_ASSERT(STRING_BUFFER_OK(sb));
str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
if (!str)
js_FinishStringBuffer(sb);
}
@ -2417,8 +2416,9 @@ EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
const jschar *cp, *start, *end;
jschar c;
length = newlength = JSSTRING_LENGTH(str);
for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) {
JSSTRING_CHARS_AND_LENGTH(str, start, length);
newlength = length;
for (cp = start, end = cp + length; cp < end; cp++) {
c = *cp;
if (c == '"')
newlength += 5;
@ -2460,7 +2460,7 @@ EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
js_AppendChar(sb, c);
}
JS_ASSERT(STRING_BUFFER_OK(sb));
str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0);
str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
if (!str)
js_FinishStringBuffer(sb);
}
@ -2579,8 +2579,8 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
* ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
* likely URI of the form ".../xbl2/2005".
*/
start = JSSTRING_CHARS(uri);
cp = end = start + JSSTRING_LENGTH(uri);
JSSTRING_CHARS_AND_END(uri, start, end);
cp = end;
while (--cp > start) {
if (*cp == '.' || *cp == '/' || *cp == ':') {
++cp;
@ -2654,7 +2654,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
offset = PTRDIFF(cp, start, jschar);
prefix = js_NewDependentString(cx, uri, offset, length);
} else {
prefix = js_NewString(cx, bp, newlength, 0);
prefix = js_NewString(cx, bp, newlength);
if (!prefix)
JS_free(cx, bp);
}
@ -2752,7 +2752,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
return NULL;
}
str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
list_out:
if (!str && STRING_BUFFER_OK(&sb))
js_FinishStringBuffer(&sb);
@ -3025,7 +3025,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
goto out;
}
str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0);
str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb));
out:
js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str));
if (!str && STRING_BUFFER_OK(&sb))
@ -7875,39 +7875,38 @@ JSString *
js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2)
{
size_t len, len2, newlen;
jschar *chars;
jschar *chars, *chars2;
if (JSSTRING_IS_DEPENDENT(str) ||
!(*js_GetGCThingFlags(str) & GCF_MUTABLE)) {
str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str));
JSSTRING_CHARS_AND_LENGTH(str, chars, len);
if (!JSSTRING_IS_MUTABLE(str)) {
str = js_NewStringCopyN(cx, chars, len);
if (!str)
return NULL;
chars = str->u.chars;
} else {
/*
* Reallocating str (because we know it has no other references)
* requires purging any deflated string cached for it.
*/
js_PurgeDeflatedStringCache(cx->runtime, str);
}
len = str->length;
len2 = JSSTRING_LENGTH(str2);
JSSTRING_CHARS_AND_LENGTH(str2, chars2, len2);
newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1;
chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar));
chars = (jschar *) JS_realloc(cx, chars, (newlen+1) * sizeof(jschar));
if (!chars)
return NULL;
/*
* Reallocating str (because we know it has no other references) requires
* purging any deflated string cached for it.
*/
js_PurgeDeflatedStringCache(cx->runtime, str);
str->chars = chars;
str->length = newlen;
JSSTRING_INIT(str, chars, newlen);
chars += len;
if (isName) {
*chars++ = ' ';
js_strncpy(chars, JSSTRING_CHARS(str2), len2);
js_strncpy(chars, chars2, len2);
chars += len2;
} else {
*chars++ = '=';
*chars++ = '"';
js_strncpy(chars, JSSTRING_CHARS(str2), len2);
js_strncpy(chars, chars2, len2);
chars += len2;
*chars++ = '"';
}