diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 8286110d6e00..c14c5755a4b4 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -1292,7 +1292,10 @@ StringToInteger(JSContext* cx, JSString* string, IntegerType* result) { JS_STATIC_ASSERT(numeric_limits::is_exact); - const jschar* cp = string->chars(); + const jschar* cp = string->getChars(NULL); + if (!cp) + return false; + const jschar* end = cp + string->length(); if (cp == end) return false; @@ -1780,9 +1783,10 @@ ImplicitConvert(JSContext* cx, JSString* str = JSVAL_TO_STRING(val); \ if (str->length() != 1) \ return TypeError(cx, #name, val); \ - \ - result = str->chars()[0]; \ - \ + const jschar *chars = str->getChars(cx); \ + if (!chars) \ + return false; \ + result = chars[0]; \ } else if (!jsvalToInteger(cx, val, &result)) { \ return TypeError(cx, #name, val); \ } \ @@ -1824,8 +1828,10 @@ ImplicitConvert(JSContext* cx, // which the caller assumes ownership of. // TODO: Extend this so we can safely convert strings at other times also. JSString* sourceString = JSVAL_TO_STRING(val); - const jschar* sourceChars = sourceString->chars(); size_t sourceLength = sourceString->length(); + const jschar* sourceChars = sourceString->getChars(cx); + if (!sourceChars) + return false; switch (CType::GetTypeCode(cx, baseType)) { case TYPE_char: @@ -1879,8 +1885,10 @@ ImplicitConvert(JSContext* cx, if (JSVAL_IS_STRING(val)) { JSString* sourceString = JSVAL_TO_STRING(val); - const jschar* sourceChars = sourceString->chars(); size_t sourceLength = sourceString->length(); + const jschar* sourceChars = sourceString->getChars(cx); + if (!sourceChars) + return false; switch (CType::GetTypeCode(cx, baseType)) { case TYPE_char: @@ -1989,21 +1997,18 @@ ImplicitConvert(JSContext* cx, if (JSID_IS_VOID(id)) break; - js::AutoValueRooter fieldVal(cx); - JS_IdToValue(cx, id, fieldVal.jsval_addr()); - if (!JSVAL_IS_STRING(fieldVal.jsval_value())) { + if (!JSID_IS_STRING(id)) { JS_ReportError(cx, "property name is not a string"); return false; } - const FieldInfo* field = StructType::LookupField(cx, targetType, - JSVAL_TO_STRING(fieldVal.jsval_value())); + JSFlatString *name = JSID_TO_FLAT_STRING(id); + const FieldInfo* field = StructType::LookupField(cx, targetType, name); if (!field) return false; - JSString* name = JSVAL_TO_STRING(fieldVal.jsval_value()); js::AutoValueRooter prop(cx); - if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), prop.jsval_addr())) + if (!JS_GetPropertyById(cx, obj, id, prop.jsval_addr())) return false; // Convert the field via ImplicitConvert(). @@ -3567,8 +3572,10 @@ ArrayType::ConstructData(JSContext* cx, // We were given a string. Size the array to the appropriate length, // including space for the terminator. JSString* sourceString = JSVAL_TO_STRING(argv[0]); - const jschar* sourceChars = sourceString->chars(); size_t sourceLength = sourceString->length(); + const jschar* sourceChars = sourceString->getChars(cx); + if (!sourceChars) + return false; switch (CType::GetTypeCode(cx, baseType)) { case TYPE_char: @@ -3871,7 +3878,7 @@ ArrayType::AddressOfElement(JSContext* cx, uintN argc, jsval* vp) // For a struct field descriptor 'val' of the form { name : type }, extract // 'name' and 'type'. -static JSString* +static JSFlatString* ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) { if (JSVAL_IS_PRIMITIVE(val)) { @@ -3885,23 +3892,21 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) return NULL; js::AutoObjectRooter iterroot(cx, iter); - jsid id; - if (!JS_NextProperty(cx, iter, &id)) + jsid nameid; + if (!JS_NextProperty(cx, iter, &nameid)) return NULL; - if (JSID_IS_VOID(id)) { + if (JSID_IS_VOID(nameid)) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); return NULL; } - js::AutoValueRooter nameVal(cx); - JS_IdToValue(cx, id, nameVal.jsval_addr()); - if (!JSVAL_IS_STRING(nameVal.jsval_value())) { + if (!JSID_IS_STRING(nameid)) { JS_ReportError(cx, "struct field descriptors require a valid name and type"); return NULL; } - JSString* name = JSVAL_TO_STRING(nameVal.jsval_value()); // make sure we have one, and only one, property + jsid id; if (!JS_NextProperty(cx, iter, &id)) return NULL; if (!JSID_IS_VOID(id)) { @@ -3910,7 +3915,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) } js::AutoValueRooter propVal(cx); - if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), propVal.jsval_addr())) + if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr())) return NULL; if (propVal.value().isPrimitive() || @@ -3929,7 +3934,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) return NULL; } - return name; + return JSID_TO_FLAT_STRING(nameid); } // For a struct field with 'name' and 'type', add an element of the form @@ -3937,7 +3942,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) static JSBool AddFieldToArray(JSContext* cx, jsval* element, - JSString* name, + JSFlatString* name, JSObject* typeObj) { JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL); @@ -4048,7 +4053,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj return JS_FALSE; JSObject* fieldType = NULL; - JSString* name = ExtractStructField(cx, item.jsval_value(), &fieldType); + JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType); if (!name) return JS_FALSE; fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType); @@ -4321,7 +4326,7 @@ StructType::GetFieldInfo(JSContext* cx, JSObject* obj) } const FieldInfo* -StructType::LookupField(JSContext* cx, JSObject* obj, JSString *name) +StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString *name) { JS_ASSERT(CType::IsCType(cx, obj)); JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); @@ -4417,7 +4422,7 @@ StructType::FieldGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) return JS_FALSE; } - const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_STRING(idval)); + const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); if (!field) return JS_FALSE; @@ -4439,7 +4444,7 @@ StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp) return JS_FALSE; } - const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_STRING(idval)); + const FieldInfo* field = LookupField(cx, typeObj, JSID_TO_FLAT_STRING(idval)); if (!field) return JS_FALSE; @@ -4467,8 +4472,11 @@ StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp) return JS_FALSE; } - const FieldInfo* field = LookupField(cx, typeObj, - JSVAL_TO_STRING(JS_ARGV(cx, vp)[0])); + JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0])); + if (!str) + return JS_FALSE; + + const FieldInfo* field = LookupField(cx, typeObj, str); if (!field) return JS_FALSE; @@ -4611,18 +4619,23 @@ PrepareReturnType(JSContext* cx, jsval type) return result; } -static JS_ALWAYS_INLINE bool -IsEllipsis(jsval v) +static JS_ALWAYS_INLINE JSBool +IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis) { + *isEllipsis = false; if (!JSVAL_IS_STRING(v)) - return false; + return true; JSString* str = JSVAL_TO_STRING(v); if (str->length() != 3) + return true; + const jschar* chars = str->getChars(cx); + if (!chars) return false; - const jschar* chars = str->chars(), dot('.'); - return (chars[0] == dot && - chars[1] == dot && - chars[2] == dot); + jschar dot = '.'; + *isEllipsis = (chars[0] == dot && + chars[1] == dot && + chars[2] == dot); + return true; } static JSBool @@ -4737,7 +4750,10 @@ NewFunctionInfo(JSContext* cx, fninfo->mIsVariadic = false; for (JSUint32 i = 0; i < argLength; ++i) { - if (IsEllipsis(argTypes[i])) { + bool isEllipsis; + if (!IsEllipsis(cx, argTypes[i], &isEllipsis)) + return false; + if (isEllipsis) { fninfo->mIsVariadic = true; if (i < 1) { JS_ReportError(cx, "\"...\" may not be the first and only parameter " diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 238dad269c29..76cf9fa8112f 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -141,7 +141,10 @@ void AppendString(Vector &v, JSString* str) { JS_ASSERT(str); - v.append(str->chars(), str->length()); + const jschar *chars = str->getChars(NULL); + if (!chars) + return; + v.append(chars, str->length()); } template @@ -154,8 +157,12 @@ AppendString(Vector &v, JSString* str) if (!v.resize(vlen + alen)) return; + const jschar *chars = str->getChars(NULL); + if (!chars) + return; + for (size_t i = 0; i < alen; ++i) - v[i + vlen] = char(str->chars()[i]); + v[i + vlen] = char(chars[i]); } template @@ -186,33 +193,15 @@ PrependString(Vector &v, JSString* str) if (!v.resize(vlen + alen)) return; + const jschar *chars = str->getChars(NULL); + if (!chars) + return; + // Move vector data forward. This is safe since we've already resized. memmove(v.begin() + alen, v.begin(), vlen * sizeof(jschar)); // Copy data to insert. - memcpy(v.begin(), str->chars(), alen * sizeof(jschar)); -} - -template -bool -StringsEqual(Vector &v, Vector &w) -{ - if (v.length() != w.length()) - return false; - - return memcmp(v.begin(), w.begin(), v.length() * sizeof(T)) == 0; -} - -template -bool -StringsEqual(Vector &v, JSString* str) -{ - JS_ASSERT(str); - size_t length = str->length(); - if (v.length() != length) - return false; - - return memcmp(v.begin(), str->chars(), length * sizeof(jschar)) == 0; + memcpy(v.begin(), chars, alen * sizeof(jschar)); } /******************************************************************************* @@ -274,7 +263,7 @@ struct FieldInfo // Hash policy for FieldInfos. struct FieldHashPolicy { - typedef JSString* Key; + typedef JSFlatString* Key; typedef Key Lookup; static uint32 hash(const Lookup &l) { @@ -297,7 +286,7 @@ struct FieldHashPolicy } }; -typedef HashMap FieldInfoHash; +typedef HashMap FieldInfoHash; // Descriptor of ABI, return type, argument types, and variadicity for a // FunctionType. @@ -482,7 +471,7 @@ namespace StructType { JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj); const FieldInfoHash* GetFieldInfo(JSContext* cx, JSObject* obj); - const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSString *name); + const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString *name); JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj); ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); } diff --git a/js/src/ctypes/Library.cpp b/js/src/ctypes/Library.cpp index 5b85e0ceb863..ae021634f51a 100644 --- a/js/src/ctypes/Library.cpp +++ b/js/src/ctypes/Library.cpp @@ -133,12 +133,13 @@ Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks) } PRLibSpec libSpec; - JSString* pathStr = JSVAL_TO_STRING(path); + JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path)); + if (!pathStr) + return NULL; #ifdef XP_WIN // On Windows, converting to native charset may corrupt path string. // So, we have to use Unicode path directly. - const PRUnichar* pathChars = reinterpret_cast( - JS_GetStringCharsZ(cx, pathStr)); + const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr); if (!pathChars) return NULL; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 59c8b521cd1f..38c973c854f4 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -584,16 +584,14 @@ JS_PUBLIC_API(JSBool) JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal) { assertSameCompartment(cx, v1, v2); - *equal = StrictlyEqual(cx, Valueify(v1), Valueify(v2)); - return JS_TRUE; + return StrictlyEqual(cx, Valueify(v1), Valueify(v2), equal); } JS_PUBLIC_API(JSBool) JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same) { assertSameCompartment(cx, v1, v2); - *same = SameValue(Valueify(v1), Valueify(v2), cx); - return JS_TRUE; + return SameValue(cx, Valueify(v1), Valueify(v2), same); } /************************************************************************/ @@ -2239,8 +2237,14 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui } case JSTRACE_STRING: - PutEscapedString(buf, bufsize, (JSString *)thing, 0); + { + JSString *str = (JSString *)thing; + if (str->isLinear()) + PutEscapedString(buf, bufsize, str->assertIsLinear(), 0); + else + JS_snprintf(buf, bufsize, "", (int)str->length()); break; + } #if JS_HAS_XML_SUPPORT case JSTRACE_XML: @@ -5208,17 +5212,8 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) JS_PUBLIC_API(JSString *) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) { - jschar *js; - JSString *str; - CHECK_REQUEST(cx); - js = js_InflateString(cx, s, &n); - if (!js) - return NULL; - str = js_NewString(cx, js, n); - if (!str) - cx->free(js); - return str; + return js_NewStringCopyN(cx, s, n); } JS_PUBLIC_API(JSString *) @@ -5338,9 +5333,8 @@ JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength) { CHECK_REQUEST(cx); assertSameCompartment(cx, str); - const jschar *chars; - str->getCharsAndLength(chars, *plength); - return chars; + *plength = str->length(); + return str->getChars(cx); } JS_PUBLIC_API(const jschar *) @@ -5369,26 +5363,29 @@ JS_FlattenString(JSContext *cx, JSString *str) extern JS_PUBLIC_API(const jschar *) JS_GetFlatStringChars(JSFlatString *str) { - return reinterpret_cast(str)->flatChars(); + return str->chars(); } JS_PUBLIC_API(JSBool) JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result) { - *result = js_CompareStrings(str1, str2); - return JS_TRUE; + return CompareStrings(cx, str1, str2, result); } JS_PUBLIC_API(JSBool) JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match) { - return MatchStringAndAscii(str, asciiBytes); + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + *match = StringEqualsAscii(linearStr, asciiBytes); + return true; } JS_PUBLIC_API(JSBool) JS_FlatStringEqualsAscii(JSFlatString *str, const char *asciiBytes) { - return MatchStringAndAscii(str, asciiBytes); + return StringEqualsAscii(str, asciiBytes); } JS_PUBLIC_API(size_t) @@ -5400,13 +5397,17 @@ JS_PutEscapedFlatString(char *buffer, size_t size, JSFlatString *str, char quote JS_PUBLIC_API(size_t) JS_PutEscapedString(JSContext *cx, char *buffer, size_t size, JSString *str, char quote) { - return PutEscapedString(buffer, size, str, quote); + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return size_t(-1); + return PutEscapedString(buffer, size, linearStr, quote); } JS_PUBLIC_API(JSBool) JS_FileEscapedString(FILE *fp, JSString *str, char quote) { - return FileEscapedString(fp, str, quote); + JSLinearString *linearStr = str->ensureLinear(NULL); + return linearStr && FileEscapedString(fp, linearStr, quote); } JS_PUBLIC_API(JSString *) @@ -5470,13 +5471,19 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_ JS_PUBLIC_API(char *) JS_EncodeString(JSContext *cx, JSString *str) { - return js_DeflateString(cx, str->chars(), str->length()); + const jschar *chars = str->getChars(cx); + if (!chars) + return NULL; + return js_DeflateString(cx, chars, str->length()); } JS_PUBLIC_API(size_t) JS_GetStringEncodingLength(JSContext *cx, JSString *str) { - return js_GetDeflatedStringLength(cx, str->chars(), str->length()); + const jschar *chars = str->getChars(cx); + if (!chars) + return size_t(-1); + return js_GetDeflatedStringLength(cx, chars, str->length()); } JS_PUBLIC_API(size_t) @@ -5488,12 +5495,15 @@ JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length) * error. */ size_t writtenLength = length; - if (js_DeflateStringToBuffer(NULL, str->chars(), str->length(), buffer, &writtenLength)) { + const jschar *chars = str->getChars(NULL); + if (!chars) + return size_t(-1); + if (js_DeflateStringToBuffer(NULL, chars, str->length(), buffer, &writtenLength)) { JS_ASSERT(writtenLength <= length); return writtenLength; } JS_ASSERT(writtenLength <= length); - size_t necessaryLength = js_GetDeflatedStringLength(NULL, str->chars(), str->length()); + size_t necessaryLength = js_GetDeflatedStringLength(NULL, chars, str->length()); if (necessaryLength == size_t(-1)) return size_t(-1); if (writtenLength != length) { diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 66f523e69f83..a2876e75bb0f 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -143,10 +143,10 @@ ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj) * 'id' is passed as a jsboxedword since the given id need not necessarily hold * an atomized string. */ -JSBool -js_StringIsIndex(JSString *str, jsuint *indexp) +bool +js_StringIsIndex(JSLinearString *str, jsuint *indexp) { - jschar *cp = str->chars(); + const jschar *cp = str->chars(); if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) { jsuint index = JS7_UNDEC(*cp++); jsuint oldIndex = 0; @@ -166,10 +166,10 @@ js_StringIsIndex(JSString *str, jsuint *indexp) (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) { *indexp = index; - return JS_TRUE; + return true; } } - return JS_FALSE; + return false; } static bool @@ -1149,12 +1149,13 @@ array_toSource(JSContext *cx, uintN argc, Value *vp) goto out; } vp->setString(str); - const jschar *chars; - size_t charlen; - str->getCharsAndLength(chars, charlen); + + const jschar *chars = str->getChars(cx); + if (!chars) + goto out; /* Append element to buffer. */ - if (!cb.append(chars, charlen)) + if (!cb.append(chars, chars + str->length())) goto out; if (index + 1 != length) { if (!js_AppendLiteral(cb, ", ")) @@ -1188,6 +1189,20 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, { JS_CHECK_RECURSION(cx, return false); + /* Get characters to use for the separator. */ + static const jschar comma = ','; + const jschar *sep; + size_t seplen; + if (sepstr) { + seplen = sepstr->length(); + sep = sepstr->getChars(cx); + if (!sep) + return false; + } else { + sep = , + seplen = 1; + } + /* * Use HashTable entry as the cycle indicator. On first visit, create the * entry, and, when leaving, remove the entry. @@ -1197,10 +1212,8 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, uint32 genBefore; if (!hashp) { /* Not in hash table, so not a cycle. */ - if (!cx->busyArrays.add(hashp, obj)) { - JS_ReportOutOfMemory(cx); + if (!cx->busyArrays.add(hashp, obj)) return false; - } genBefore = cx->busyArrays.generation(); } else { /* Cycle, so return empty string. */ @@ -1214,17 +1227,6 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, MUST_FLOW_THROUGH("out"); bool ok = false; - /* Get characters to use for the separator. */ - static const jschar comma = ','; - const jschar *sep; - size_t seplen; - if (sepstr) { - sepstr->getCharsAndLength(sep, seplen); - } else { - sep = , - seplen = 1; - } - /* * This object will take responsibility for the jschar buffer until the * buffer is transferred to the returned JSString. @@ -1710,15 +1712,10 @@ comparator_stack_cast(JSRedComparator func) static int sort_compare_strings(void *arg, const void *a, const void *b, int *result) { - const Value *av = (const Value *)a, *bv = (const Value *)b; - - JS_ASSERT(av->isString()); - JS_ASSERT(bv->isString()); - if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg)) - return JS_FALSE; - - *result = (int) js_CompareStrings(av->toString(), bv->toString()); - return JS_TRUE; + JSContext *cx = (JSContext *)arg; + JSString *astr = ((const Value *)a)->toString(); + JSString *bstr = ((const Value *)b)->toString(); + return JS_CHECK_OPERATION_LIMIT(cx) && CompareStrings(cx, astr, bstr, result); } JSBool @@ -2615,9 +2612,14 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp) !GetElement(cx, obj, (jsuint)i, &hole, vp)) { return JS_FALSE; } - if (!hole && StrictlyEqual(cx, *vp, tosearch)) { - vp->setNumber(i); - return JS_TRUE; + if (!hole) { + JSBool equal; + if (!StrictlyEqual(cx, *vp, tosearch, &equal)) + return JS_FALSE; + if (equal) { + vp->setNumber(i); + return JS_TRUE; + } } if (i == stop) goto not_found; diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 3a101196161d..a230b50fd56d 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -44,6 +44,7 @@ */ #include "jsprvtd.h" #include "jspubtd.h" +#include "jsatom.h" #include "jsobj.h" #include "jsstr.h" @@ -87,8 +88,8 @@ JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra) return growSlots(cx, requiredCapacity) ? ED_OK : ED_FAILED; } -extern JSBool -js_StringIsIndex(JSString *str, jsuint *indexp); +extern bool +js_StringIsIndex(JSLinearString *str, jsuint *indexp); inline JSBool js_IdIsIndex(jsid id, jsuint *indexp) @@ -105,26 +106,36 @@ js_IdIsIndex(jsid id, jsuint *indexp) if (JS_UNLIKELY(!JSID_IS_STRING(id))) return JS_FALSE; - return js_StringIsIndex(JSID_TO_STRING(id), indexp); + return js_StringIsIndex(JSID_TO_ATOM(id), indexp); } /* XML really wants to pretend jsvals are jsids. */ -inline JSBool -js_IdValIsIndex(jsval id, jsuint *indexp) +inline bool +js_IdValIsIndex(JSContext *cx, jsval id, jsuint *indexp, bool *isIndex) { if (JSVAL_IS_INT(id)) { jsint i; i = JSVAL_TO_INT(id); - if (i < 0) - return JS_FALSE; + if (i < 0) { + *isIndex = false; + return true; + } *indexp = (jsuint)i; - return JS_TRUE; + *isIndex = true; + return true; } - if (!JSVAL_IS_STRING(id)) - return JS_FALSE; + if (!JSVAL_IS_STRING(id)) { + *isIndex = false; + return true; + } - return js_StringIsIndex(JSVAL_TO_STRING(id), indexp); + JSLinearString *str = JSVAL_TO_STRING(id)->ensureLinear(cx); + if (!str) + return false; + + *isIndex = js_StringIsIndex(str, indexp); + return true; } extern js::Class js_ArrayClass, js_SlowArrayClass; diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index a7afde4ebc9a..ccd598339a55 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -457,17 +457,20 @@ js_SweepAtomState(JSContext *cx) } JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags) +js_AtomizeString(JSContext *cx, JSString *strArg, uintN flags) { JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); - if (str->isAtomized()) - return STRING_TO_ATOM(str); + if (strArg->isAtomized()) + return STRING_TO_ATOM(strArg); - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + JSLinearString *str = strArg->ensureLinear(cx); + if (!str) + return NULL; + + const jschar *chars = str->chars(); + size_t length = str->length(); JSString *staticStr = JSString::lookupStaticString(chars, length); if (staticStr) @@ -482,7 +485,7 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) /* Hashing the string should have flattened it if it was a rope. */ JS_ASSERT(str->isFlat() || str->isDependent()); - JSString *key; + JSLinearString *key; if (p) { key = AtomEntryToKey(*p); } else { @@ -506,9 +509,8 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) } else { if (needNewString) { SwitchToCompartment sc(cx, cx->runtime->defaultCompartment); - jschar *chars = str->chars(); if (flags & ATOM_NOCOPY) { - key = js_NewString(cx, chars, length); + key = js_NewString(cx, const_cast(str->flatChars()), length); if (!key) return NULL; @@ -537,7 +539,6 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags) AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED)); - JS_ASSERT(key->isAtomized()); JSAtom *atom = STRING_TO_ATOM(key); return atom; } @@ -607,7 +608,7 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) state = &cx->runtime->atomState; JS_LOCK(cx, &state->lock); - AtomSet::Ptr p = state->atoms.lookup(&str); + AtomSet::Ptr p = state->atoms.lookup(str.assertIsFlat()); str2 = p ? AtomEntryToKey(*p) : NULL; JS_UNLOCK(cx, &state->lock); @@ -628,7 +629,7 @@ js_DumpAtoms(JSContext *cx, FILE *fp) if (entry == 0) { fputs("", fp); } else { - JSString *key = AtomEntryToKey(entry); + JSAtom *key = AtomEntryToKey(entry); FileEscapedString(fp, key, '"'); uintN flags = AtomEntryFlags(entry); if (flags != 0) { diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 9ff3e1d937a3..fd897ae52cda 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -60,7 +60,7 @@ #define STRING_TO_ATOM(str) (JS_ASSERT(str->isAtomized()), \ (JSAtom *)str) -#define ATOM_TO_STRING(atom) (JS_ASSERT_STRING_IS_FLAT((JSString *)(atom))) +#define ATOM_TO_STRING(atom) (atom) #define ATOM_TO_JSVAL(atom) STRING_TO_JSVAL(ATOM_TO_STRING(atom)) /* Engine-internal extensions of jsid */ @@ -265,23 +265,23 @@ JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JS_GCTHING_ALIGN); typedef uintptr_t AtomEntryType; -static JS_ALWAYS_INLINE JSString * +static JS_ALWAYS_INLINE JSAtom * AtomEntryToKey(AtomEntryType entry) { JS_ASSERT(entry != 0); - return (JSString *)(entry & ~ATOM_ENTRY_FLAG_MASK); + return (JSAtom *)(entry & ~ATOM_ENTRY_FLAG_MASK); } struct AtomHasher { - typedef JSString *Lookup; + typedef JSLinearString *Lookup; - static HashNumber hash(JSString *str) { + static HashNumber hash(JSLinearString *str) { return js_HashString(str); } - static bool match(AtomEntryType entry, JSString *lookup) { - return entry ? js_EqualStrings(AtomEntryToKey(entry), lookup) : false; + static bool match(AtomEntryType entry, JSLinearString *lookup) { + return entry ? EqualStrings(AtomEntryToKey(entry), lookup) : false; } }; diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index f16aa8b4d806..b83237a20393 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -168,18 +168,22 @@ js_DoubleToUint32(jsdouble d) JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, ACCSET_NONE) jsdouble FASTCALL -js_StringToNumber(JSContext* cx, JSString* str) +js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok) { - return StringToNumberType(cx, str); + double out = 0; /* silence warnings. */ + *ok = StringToNumberType(cx, str, &out); + return out; } -JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, 1, ACCSET_NONE) +JS_DEFINE_CALLINFO_3(extern, DOUBLE, js_StringToNumber, CONTEXT, STRING, BOOLPTR, 1, ACCSET_NONE) int32 FASTCALL -js_StringToInt32(JSContext* cx, JSString* str) +js_StringToInt32(JSContext* cx, JSString* str, JSBool *ok) { - return StringToNumberType(cx, str); + int32 out = 0; /* silence warnings. */ + *ok = StringToNumberType(cx, str, &out); + return out; } -JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, ACCSET_NONE) +JS_DEFINE_CALLINFO_3(extern, INT32, js_StringToInt32, CONTEXT, STRING, BOOLPTR, 1, ACCSET_NONE) /* Nb: it's always safe to set isDefinitelyAtom to false if you're unsure or don't know. */ static inline JSBool diff --git a/js/src/jsbuiltins.h b/js/src/jsbuiltins.h index 14c83e0982b1..9d372a3c86c1 100644 --- a/js/src/jsbuiltins.h +++ b/js/src/jsbuiltins.h @@ -547,7 +547,7 @@ struct ClosureVarInfo; #define _JS_DEFINE_CALLINFO_n(n, args) JS_DEFINE_CALLINFO_##n args jsdouble FASTCALL -js_StringToNumber(JSContext* cx, JSString* str); +js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok); /* Extern version of SetBuiltinError. */ extern JS_FRIEND_API(void) @@ -623,9 +623,9 @@ JS_DECLARE_CALLINFO(js_CloneRegExpObject) /* Defined in jsstr.cpp. */ JS_DECLARE_CALLINFO(js_String_tn) -JS_DECLARE_CALLINFO(js_CompareStrings) +JS_DECLARE_CALLINFO(js_CompareStringsOnTrace) JS_DECLARE_CALLINFO(js_ConcatStrings) -JS_DECLARE_CALLINFO(js_EqualStrings) +JS_DECLARE_CALLINFO(js_EqualStringsOnTrace) JS_DECLARE_CALLINFO(js_Flatten) /* Defined in jstypedarray.cpp. */ diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index f49bf9cb0ae3..b99d08f6b612 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -350,9 +350,10 @@ JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); bool JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str) { - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->getChars(context()); + if (!chars) + return false; return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length); } @@ -750,9 +751,10 @@ JSStructuredCloneReader::startRead(Value *vp) JSString *str = readString(nchars); if (!str) return false; - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->getChars(context()); + if (!chars) + return false; JSObject *obj = RegExp::createObjectNoStatics(context(), chars, length, data); if (!obj) return false; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 0fb97842aa37..3caac9750d7f 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1369,7 +1369,7 @@ struct JSRuntime { js::Value negativeInfinityValue; js::Value positiveInfinityValue; - JSString *emptyString; + JSFlatString *emptyString; /* List of active contexts sharing this runtime; protected by gcLock. */ JSCList contextList; @@ -2386,10 +2386,10 @@ struct JSContext #ifdef XP_WIN volatile DollarPath *dollarPath; volatile JSSubString *sub; - volatile jschar *blackBox; - volatile jschar **repstrChars; - volatile jschar **repstrDollar; - volatile jschar **repstrDollarEnd; + volatile const jschar *blackBox; + volatile const jschar **repstrChars; + volatile const jschar **repstrDollar; + volatile const jschar **repstrDollarEnd; volatile size_t *peekLen; #endif diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d75088c2ba5d..f74a36da3f11 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -204,7 +204,10 @@ JSCompartment::wrap(JSContext *cx, Value *vp) if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); - JSString *wrapped = js_NewStringCopyN(cx, str->chars(), str->length()); + const jschar *chars = str->getChars(cx); + if (!chars) + return false; + JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 3d73242aec94..f25c5ad60f6d 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -750,7 +750,7 @@ ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit) */ static JSBool -date_parseISOString(JSString *str, jsdouble *result, JSContext *cx) +date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx) { jsdouble msec; @@ -792,7 +792,8 @@ date_parseISOString(JSString *str, jsdouble *result, JSContext *cx) if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \ JS_END_MACRO - str->getCharsAndLength(s, limit); + s = str->chars(); + limit = str->length(); if (PEEK('+') || PEEK('-')) { if (PEEK('-')) @@ -883,7 +884,7 @@ date_parseISOString(JSString *str, jsdouble *result, JSContext *cx) } static JSBool -date_parseString(JSString *str, jsdouble *result, JSContext *cx) +date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx) { jsdouble msec; @@ -907,7 +908,8 @@ date_parseString(JSString *str, jsdouble *result, JSContext *cx) if (date_parseISOString(str, result, cx)) return JS_TRUE; - str->getCharsAndLength(s, limit); + s = str->chars(); + limit = str->length(); if (limit == 0) goto syntax; while (i < limit) { @@ -1167,7 +1169,11 @@ date_parse(JSContext *cx, uintN argc, Value *vp) if (!str) return JS_FALSE; vp[2].setString(str); - if (!date_parseString(str, &result, cx)) { + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + + if (!date_parseString(linearStr, &result, cx)) { vp->setDouble(js_NaN); return true; } @@ -2377,8 +2383,6 @@ date_toString(JSContext *cx, uintN argc, Value *vp) static JSBool date_valueOf(JSContext *cx, uintN argc, Value *vp) { - JSString *str, *number_str; - /* It is an error to call date_valueOf on a non-date object, but we don't * need to check for that explicitly here because every path calls * GetUTCTime, which does the check. @@ -2389,11 +2393,14 @@ date_valueOf(JSContext *cx, uintN argc, Value *vp) return date_getTime(cx, argc, vp); /* Convert to number only if the hint was given, otherwise favor string. */ - str = js_ValueToString(cx, vp[2]); + JSString *str = js_ValueToString(cx, vp[2]); if (!str) return JS_FALSE; - number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); - if (js_EqualStrings(str, number_str)) + JSLinearString *linear_str = str->ensureLinear(cx); + if (!linear_str) + return JS_FALSE; + JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]; + if (EqualStrings(linear_str, number_str)) return date_getTime(cx, argc, vp); return date_toString(cx, argc, vp); } @@ -2487,8 +2494,11 @@ js_Date(JSContext *cx, uintN argc, Value *vp) if (!str) return false; argv[0].setString(str); + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; - if (!date_parseString(str, &d, cx)) + if (!date_parseString(linearStr, &d, cx)) d = js_NaN; else d = TIMECLIP(d); diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 893d2ab21c6f..d4b26ae6f98b 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -612,10 +612,11 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv) #define APPEND_STRING_TO_STACK(str) \ JS_BEGIN_MACRO \ JSString *str_ = str; \ - const jschar *chars_; \ - size_t length_; \ + size_t length_ = str_->length(); \ + const jschar *chars_ = str_->getChars(cx); \ + if (!chars_) \ + goto bad; \ \ - str_->getCharsAndLength(chars_, length_); \ if (length_ > stackmax - stacklen) { \ void *ptr_; \ if (stackmax >= STACK_LENGTH_LIMIT || \ @@ -813,11 +814,17 @@ exn_toString(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; if (name_length) { - js_strncpy(cp, name->chars(), name_length); + const jschar *name_chars = name->getChars(cx); + if (!name_chars) + return JS_FALSE; + js_strncpy(cp, name_chars, name_length); cp += name_length; *cp++ = ':'; *cp++ = ' '; } - js_strncpy(cp, message->chars(), message_length); + const jschar *message_chars = message->getChars(cx); + if (!message_chars) + return JS_FALSE; + js_strncpy(cp, message_chars, message_length); cp += message_length; *cp = 0; @@ -917,18 +924,27 @@ exn_toSource(JSContext *cx, uintN argc, Value *vp) return false; *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; - js_strncpy(cp, name->chars(), name_length); + const jschar *name_chars = name->getChars(cx); + if (!name_chars) + return false; + js_strncpy(cp, name_chars, name_length); cp += name_length; *cp++ = '('; + const jschar *message_chars = message->getChars(cx); + if (!message_chars) + return false; if (message_length != 0) { - js_strncpy(cp, message->chars(), message_length); + js_strncpy(cp, message_chars, message_length); cp += message_length; } if (filename_length != 0) { /* append filename as ``, {filename}'' */ *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, filename->chars(), filename_length); + const jschar *filename_chars = filename->getChars(cx); + if (!filename_chars) + return false; + js_strncpy(cp, filename_chars, filename_length); cp += filename_length; } else { if (lineno_as_str) { @@ -942,7 +958,10 @@ exn_toSource(JSContext *cx, uintN argc, Value *vp) if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, lineno_as_str->chars(), lineno_length); + const jschar *lineno_chars = lineno_as_str->getChars(cx); + if (!lineno_chars) + return false; + js_strncpy(cp, lineno_chars, lineno_length); cp += lineno_length; } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index d1d52c5f12e5..5efd454798b3 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -2603,7 +2603,12 @@ Function(JSContext *cx, uintN argc, Value *vp) for (uintN i = 0; i < n; i++) { JSString *arg = argv[i].toString(); size_t arg_length = arg->length(); - (void) js_strncpy(cp, arg->chars(), arg_length); + const jschar *arg_chars = arg->getChars(cx); + if (!arg_chars) { + JS_ARENA_RELEASE(&cx->tempPool, mark); + return JS_FALSE; + } + (void) js_strncpy(cp, arg_chars, arg_length); cp += arg_length; /* Add separating comma or terminating 0. */ @@ -2690,8 +2695,11 @@ Function(JSContext *cx, uintN argc, Value *vp) str = cx->runtime->emptyString; } - return Compiler::compileFunctionBody(cx, fun, principals, - str->chars(), str->length(), + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; + return Compiler::compileFunctionBody(cx, fun, principals, chars, length, filename, lineno); } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 1e062d44053a..a72ebd54dcaa 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1767,7 +1767,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str) JS_ASSERT(IsFinalizableStringKind(thingKind)); /* A stillborn string has null chars, so is not valid. */ - jschar *chars = str->flatChars(); + jschar *chars = const_cast(str->flatChars()); if (!chars) return; if (thingKind == FINALIZE_STRING) { diff --git a/js/src/jsgcstats.cpp b/js/src/jsgcstats.cpp index 85022aa191de..56b48ab3c98b 100644 --- a/js/src/jsgcstats.cpp +++ b/js/src/jsgcstats.cpp @@ -362,9 +362,13 @@ GCMarker::dumpConservativeRoots() } case JSTRACE_STRING: { JSString *str = (JSString *) i->thing; - char buf[50]; - PutEscapedString(buf, sizeof buf, str, '"'); - fprintf(fp, "string %s", buf); + if (str->isLinear()) { + char buf[50]; + PutEscapedString(buf, sizeof buf, str->assertIsLinear(), '"'); + fprintf(fp, "string %s", buf); + } else { + fprintf(fp, "rope: length %d", (int)str->length()); + } break; } # if JS_HAS_XML_SUPPORT diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index abab49f2c248..b0a1d1f18fa9 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1130,40 +1130,44 @@ HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) return JS_FALSE; } -static JS_ALWAYS_INLINE bool -EqualObjects(JSContext *cx, JSObject *lobj, JSObject *robj) -{ - return lobj == robj; -} - bool -StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref) +StrictlyEqual(JSContext *cx, const Value &lref, const Value &rref, JSBool *equal) { Value lval = lref, rval = rref; if (SameType(lval, rval)) { if (lval.isString()) - return js_EqualStrings(lval.toString(), rval.toString()); - if (lval.isDouble()) - return JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE); - if (lval.isObject()) - return EqualObjects(cx, &lval.toObject(), &rval.toObject()); - if (lval.isUndefined()) - return true; - return lval.payloadAsRawUint32() == rval.payloadAsRawUint32(); + return EqualStrings(cx, lval.toString(), rval.toString(), equal); + if (lval.isDouble()) { + *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE); + return true; + } + if (lval.isObject()) { + *equal = &lval.toObject() == &rval.toObject(); + return true; + } + if (lval.isUndefined()) { + *equal = true; + return true; + } + *equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32(); + return true; } if (lval.isDouble() && rval.isInt32()) { double ld = lval.toDouble(); double rd = rval.toInt32(); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + return true; } if (lval.isInt32() && rval.isDouble()) { double ld = lval.toInt32(); double rd = rval.toDouble(); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + *equal = JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + return true; } - return false; + *equal = false; + return true; } static inline bool @@ -1179,15 +1183,21 @@ IsNaN(const Value &v) } bool -SameValue(const Value &v1, const Value &v2, JSContext *cx) +SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same) { - if (IsNegativeZero(v1)) - return IsNegativeZero(v2); - if (IsNegativeZero(v2)) - return false; - if (IsNaN(v1) && IsNaN(v2)) + if (IsNegativeZero(v1)) { + *same = IsNegativeZero(v2); return true; - return StrictlyEqual(cx, v1, v2); + } + if (IsNegativeZero(v2)) { + *same = false; + return true; + } + if (IsNaN(v1) && IsNaN(v2)) { + *same = true; + return true; + } + return StrictlyEqual(cx, v1, v2, same); } JSType @@ -1516,7 +1526,6 @@ js_LogOpcode(JSContext *cx) JSStackFrame *fp; JSFrameRegs *regs; intN ndefs, n, nuses; - JSString *str; JSOp op; logfp = (FILE *) cx->logfp; @@ -1562,12 +1571,13 @@ js_LogOpcode(JSContext *cx) */ fputs("", logfp); } else { - str = js_ValueToString(cx, *siter); - if (!str) { + JSString *str = js_ValueToString(cx, *siter); + JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL; + if (!linearStr) { fputs("", logfp); - } else { JS_ClearPendingException(cx); - FileEscapedString(logfp, str, 0); + } else { + FileEscapedString(logfp, linearStr, 0); } } fputc(' ', logfp); @@ -3389,7 +3399,10 @@ END_CASE(JSOP_BITAND) if (SameType(lval, rval)) { \ if (lval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_EqualStrings(l, r) OP JS_TRUE; \ + JSBool equal; \ + if (!EqualStrings(cx, l, r, &equal)) \ + goto error; \ + cond = equal OP JS_TRUE; \ } else if (lval.isDouble()) { \ double l = lval.toDouble(), r = rval.toDouble(); \ cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ @@ -3418,7 +3431,10 @@ END_CASE(JSOP_BITAND) DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \ if (lval.isString() && rval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_EqualStrings(l, r) OP JS_TRUE; \ + JSBool equal; \ + if (!EqualStrings(cx, l, r, &equal)) \ + goto error; \ + cond = equal OP JS_TRUE; \ } else { \ double l, r; \ if (!ValueToNumber(cx, lval, &l) || \ @@ -3450,7 +3466,10 @@ END_CASE(JSOP_NE) JS_BEGIN_MACRO \ const Value &rref = regs.sp[-1]; \ const Value &lref = regs.sp[-2]; \ - COND = StrictlyEqual(cx, lref, rref) OP true; \ + JSBool equal; \ + if (!StrictlyEqual(cx, lref, rref, &equal)) \ + goto error; \ + COND = equal OP true; \ regs.sp--; \ JS_END_MACRO @@ -3511,7 +3530,10 @@ END_CASE(JSOP_CASEX) DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ if (lval.isString() && rval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_CompareStrings(l, r) OP 0; \ + int32 result; \ + if (!CompareStrings(cx, l, r, &result)) \ + goto error; \ + cond = result OP 0; \ } else { \ double l, r; \ if (!ValueToNumber(cx, lval, &l) || \ @@ -5094,12 +5116,14 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH) } if (lval.isString()) { - JSString *str = lval.toString(); - JSString *str2; + JSLinearString *str = lval.toString()->ensureLinear(cx); + if (!str) + goto error; + JSLinearString *str2; SEARCH_PAIRS( match = (rval.isString() && - ((str2 = rval.toString()) == str || - js_EqualStrings(str2, str))); + ((str2 = rval.toString()->assertIsLinear()) == str || + EqualStrings(str2, str))); ) } else if (lval.isNumber()) { double ldbl = lval.toNumber(); diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index f53eb5a3eefc..46d9f39fa6e8 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -1030,11 +1030,11 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, JSObject **objp, JSProperty **propp); extern bool -StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval); +StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, JSBool *equal); /* === except that NaN is the same as NaN and -0 is not the same as +0. */ extern bool -SameValue(const Value &v1, const Value &v2, JSContext *cx); +SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same); extern JSType TypeOfValue(JSContext *cx, const Value &v); diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 52bd227a485e..47d018093224 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -315,7 +315,10 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp) str = js_ValueToString(cx, vp[2]); if (!str) return JS_FALSE; - str->getCharsAndEnd(bp, end); + bp = str->getChars(cx); + if (!bp) + return JS_FALSE; + end = bp + str->length(); if (!js_strtod(cx, bp, end, &ep, &d)) return JS_FALSE; if (ep == bp) { @@ -330,12 +333,15 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp) static jsdouble FASTCALL ParseFloat(JSContext* cx, JSString* str) { - const jschar* bp; - const jschar* end; - const jschar* ep; - jsdouble d; + const jschar *bp = str->getChars(cx); + if (!bp) { + SetBuiltinError(cx); + return js_NaN; + } + const jschar *end = bp + str->length(); - str->getCharsAndEnd(bp, end); + const jschar *ep; + double d; if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp) return js_NaN; return d; @@ -451,8 +457,10 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp) } /* Steps 2-5, 9-14. */ - const jschar *ws, *end; - inputString->getCharsAndEnd(ws, end); + const jschar *ws = inputString->getChars(cx); + if (!ws) + return false; + const jschar *end = ws + inputString->length(); jsdouble number; if (!ParseIntStringHelper(cx, ws, end, radix, stripPrefix, &number)) @@ -467,8 +475,12 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp) static jsdouble FASTCALL ParseInt(JSContext* cx, JSString* str) { - const jschar *start, *end; - str->getCharsAndEnd(start, end); + const jschar *start = str->getChars(cx); + if (!start) { + SetBuiltinError(cx); + return js_NaN; + } + const jschar *end = start + str->length(); jsdouble d; if (!ParseIntStringHelper(cx, start, end, 0, true, &d)) { @@ -499,7 +511,7 @@ JS_DEFINE_TRCINFO_2(num_parseInt, (1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, nanojit::ACCSET_NONE))) JS_DEFINE_TRCINFO_1(num_parseFloat, - (2, (static, DOUBLE, ParseFloat, CONTEXT, STRING, 1, nanojit::ACCSET_NONE))) + (2, (static, DOUBLE_FAIL, ParseFloat, CONTEXT, STRING, 1, nanojit::ACCSET_NONE))) #endif /* JS_TRACER */ @@ -1184,6 +1196,14 @@ js_NumberToString(JSContext *cx, jsdouble d) return js_NumberToStringWithBase(cx, d, 10); } +JSFlatString * +js::NumberToString(JSContext *cx, jsdouble d) +{ + if (JSString *str = js_NumberToStringWithBase(cx, d, 10)) + return str->assertIsFlat(); + return NULL; +} + JSBool JS_FASTCALL js_NumberValueToCharBuffer(JSContext *cx, const Value &v, JSCharBuffer &cb) { @@ -1232,13 +1252,8 @@ ValueToNumberSlow(JSContext *cx, Value v, double *out) return true; } skip_int_double: - if (v.isString()) { - jsdouble d = StringToNumberType(cx, v.toString()); - if (JSDOUBLE_IS_NaN(d)) - break; - *out = d; - return true; - } + if (v.isString()) + return StringToNumberType(cx, v.toString(), out); if (v.isBoolean()) { if (v.toBoolean()) { *out = 1.0; diff --git a/js/src/jsnum.h b/js/src/jsnum.h index dd1492e6f5a6..0dd4c7a22bcd 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -206,6 +206,10 @@ js_NumberValueToCharBuffer(JSContext *cx, const js::Value &v, JSCharBuffer &cb); namespace js { +/* Same as js_NumberToString, different signature. */ +extern JSFlatString * +NumberToString(JSContext *cx, jsdouble d); + /* * Usually a small amount of static storage is enough, but sometimes we need * to dynamically allocate much more. This struct encapsulates that. @@ -643,35 +647,44 @@ template<> struct NumberTraits { }; template -static JS_ALWAYS_INLINE T -StringToNumberType(JSContext *cx, JSString *str) +static JS_ALWAYS_INLINE bool +StringToNumberType(JSContext *cx, JSString *str, T *result) { - if (str->length() == 1) { - jschar c = str->chars()[0]; - if ('0' <= c && c <= '9') - return NumberTraits::toSelfType(T(c - '0')); - if (JS_ISSPACE(c)) - return NumberTraits::toSelfType(T(0)); - return NumberTraits::NaN(); + size_t length = str->length(); + const jschar *chars = str->getChars(NULL); + if (!chars) + return false; + + if (length == 1) { + jschar c = chars[0]; + if ('0' <= c && c <= '9') { + *result = NumberTraits::toSelfType(T(c - '0')); + return true; + } + if (JS_ISSPACE(c)) { + *result = NumberTraits::toSelfType(T(0)); + return true; + } + *result = NumberTraits::NaN(); + return true; } - const jschar* bp; - const jschar* end; - const jschar* ep; - jsdouble d; - - str->getCharsAndEnd(bp, end); + const jschar *bp = chars; + const jschar *end = chars + length; bp = js_SkipWhiteSpace(bp, end); /* ECMA doesn't allow signed hex numbers (bug 273467). */ if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) { /* Looks like a hex number. */ const jschar *endptr; + double d; if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) || js_SkipWhiteSpace(endptr, end) != end) { - return NumberTraits::NaN(); + *result = NumberTraits::NaN(); + return true; } - return NumberTraits::toSelfType(d); + *result = NumberTraits::toSelfType(d); + return true; } /* @@ -681,12 +694,14 @@ StringToNumberType(JSContext *cx, JSString *str) * that have made it here (which can only be negative ones) will * be treated as 0 without consuming the 'x' by js_strtod. */ - if (!js_strtod(cx, bp, end, &ep, &d) || - js_SkipWhiteSpace(ep, end) != end) { - return NumberTraits::NaN(); + const jschar *ep; + double d; + if (!js_strtod(cx, bp, end, &ep, &d) || js_SkipWhiteSpace(ep, end) != end) { + *result = NumberTraits::NaN(); + return true; } - - return NumberTraits::toSelfType(d); + *result = NumberTraits::toSelfType(d); + return true; } } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ffdfd2b5e600..f16cc5c3132c 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -484,7 +484,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) JSProperty *prop; Value *val; JSString *gsop[2]; - JSString *idstr, *valstr, *str; + JSString *valstr, *str; + JSLinearString *idstr; JS_CHECK_RECURSION(cx, return JS_FALSE); @@ -570,8 +571,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) * Convert id to a value and then to a string. Decide early whether we * prefer get/set or old getter/setter syntax. */ - idstr = js_ValueToString(cx, IdToValue(id)); - if (!idstr) { + JSString *s = js_ValueToString(cx, IdToValue(id)); + if (!s || !(idstr = s->ensureLinear(cx))) { ok = JS_FALSE; goto error; } @@ -609,18 +610,23 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) * If id is a string that's not an identifier, or if it's a negative * integer, then it must be quoted. */ - bool idIsLexicalIdentifier = !!js_IsIdentifier(idstr); + bool idIsLexicalIdentifier = js_IsIdentifier(idstr); if (JSID_IS_ATOM(id) ? !idIsLexicalIdentifier : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { - idstr = js_QuoteString(cx, idstr, jschar('\'')); - if (!idstr) { + s = js_QuoteString(cx, idstr, jschar('\'')); + if (!s || !(idstr = s->ensureLinear(cx))) { ok = JS_FALSE; goto error; } vp->setString(idstr); /* local root */ } - idstr->getCharsAndLength(idstrchars, idstrlength); + idstrlength = idstr->length(); + idstrchars = idstr->getChars(cx); + if (!idstrchars) { + ok = JS_FALSE; + goto error; + } for (jsint j = 0; j < valcnt; j++) { /* @@ -637,7 +643,12 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) goto error; } localroot[j].setString(valstr); /* local root */ - valstr->getCharsAndLength(vchars, vlength); + vchars = valstr->getChars(cx); + if (!vchars) { + ok = JS_FALSE; + goto error; + } + vlength = valstr->length(); /* * If val[j] is a non-sharp object, and we're not serializing an @@ -727,10 +738,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) /* Allocate 1 + 1 at end for closing brace and terminating 0. */ chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar)); if (!chars) { - /* Save code space on error: let JS_free ignore null vsharp. */ - cx->free(vsharp); - js_free(ochars); - goto error; + chars = ochars; + goto overflow; } if (comma) { @@ -741,8 +750,10 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp) if (gsop[j]) { gsoplength = gsop[j]->length(); - js_strncpy(&chars[nchars], gsop[j]->chars(), - gsoplength); + const jschar *gsopchars = gsop[j]->getChars(cx); + if (!gsopchars) + goto overflow; + js_strncpy(&chars[nchars], gsopchars, gsoplength); nchars += gsoplength; chars[nchars++] = ' '; } @@ -987,15 +998,14 @@ js_ComputeFilename(JSContext *cx, JSStackFrame *caller, #endif static inline JSScript ** -EvalCacheHash(JSContext *cx, JSString *str) +EvalCacheHash(JSContext *cx, JSLinearString *str) { - const jschar *s; - size_t n; - uint32 h; + const jschar *s = str->chars(); + size_t n = str->length(); - str->getCharsAndLength(s, n); if (n > 100) n = 100; + uint32 h; for (h = 0; n; s++, n--) h = JS_ROTATE_LEFT32(h, 4) ^ *s; @@ -1005,7 +1015,7 @@ EvalCacheHash(JSContext *cx, JSString *str) } static JS_ALWAYS_INLINE JSScript * -EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel, +EvalCacheLookup(JSContext *cx, JSLinearString *str, JSStackFrame *caller, uintN staticLevel, JSPrincipals *principals, JSObject *scopeobj, JSScript **bucket) { /* @@ -1041,9 +1051,9 @@ EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN static * Get the source string passed for safekeeping in the * atom map by the prior eval to Compiler::compileScript. */ - JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); + JSAtom *src = script->atomMap.vector[0]; - if (src == str || js_EqualStrings(src, str)) { + if (src == str || EqualStrings(src, str)) { /* * Source matches, qualify by comparing scopeobj to the * COMPILE_N_GO-memoized parent of the first literal @@ -1184,9 +1194,11 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame if (!CheckScopeChainValidity(cx, scopeobj)) return false; - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); + JSLinearString *linearStr = str->ensureLinear(cx); + if (!linearStr) + return false; + const jschar *chars = linearStr->chars(); + size_t length = linearStr->length(); /* * If the eval string starts with '(' and ends with ')', it may be JSON. @@ -1214,9 +1226,9 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame return false; JSScript *script = NULL; - JSScript **bucket = EvalCacheHash(cx, str); + JSScript **bucket = EvalCacheHash(cx, linearStr); if (evalType == DIRECT_EVAL && caller->isFunctionFrame()) - script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, bucket); + script = EvalCacheLookup(cx, linearStr, caller, staticLevel, principals, scopeobj, bucket); /* * We can't have a callerFrame (down in js::Execute's terms) if we're in @@ -1229,9 +1241,8 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL; script = Compiler::compileScript(cx, scopeobj, callerFrame, - principals, tcflags, - chars, length, - filename, lineno, str, staticLevel); + principals, tcflags, chars, length, + filename, lineno, linearStr, staticLevel); if (!script) return false; } @@ -2040,14 +2051,20 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, if (!shape->isAccessorDescriptor()) break; - if (desc.hasGet && - !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx)) { - break; + if (desc.hasGet) { + JSBool same; + if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + break; } - if (desc.hasSet && - !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) { - break; + if (desc.hasSet) { + JSBool same; + if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + break; } } else { /* @@ -2096,8 +2113,13 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, if (!shape->isDataDescriptor()) break; - if (desc.hasValue && !SameValue(desc.value, v, cx)) - break; + JSBool same; + if (desc.hasValue) { + if (!SameValue(cx, desc.value, v, &same)) + return JS_FALSE; + if (!same) + break; + } if (desc.hasWritable && desc.writable() != shape->writable()) break; } else { @@ -2144,9 +2166,14 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, /* 8.12.9 step 10. */ JS_ASSERT(shape->isDataDescriptor()); if (!shape->configurable() && !shape->writable()) { - if ((desc.hasWritable && desc.writable()) || - (desc.hasValue && !SameValue(desc.value, v, cx))) { + if (desc.hasWritable && desc.writable()) return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); + if (desc.hasValue) { + JSBool same; + if (!SameValue(cx, desc.value, v, &same)) + return JS_FALSE; + if (!same) + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); } } @@ -2155,11 +2182,20 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc, /* 8.12.9 step 11. */ JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor()); if (!shape->configurable()) { - if ((desc.hasSet && - !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) || - (desc.hasGet && - !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx))) { - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); + if (desc.hasSet) { + JSBool same; + if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); + } + + if (desc.hasGet) { + JSBool same; + if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same)) + return JS_FALSE; + if (!same) + return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); } } } @@ -6312,7 +6348,7 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize) if (JSID_IS_INT(id)) { JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id)); } else if (JSID_IS_ATOM(id)) { - PutEscapedString(buf, bufsize, JSID_TO_STRING(id), 0); + PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); } else { JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); } @@ -6505,15 +6541,22 @@ js_DumpChars(const jschar *s, size_t n) void dumpString(JSString *str) { - dumpChars(str->chars(), str->length()); + if (const jschar *chars = str->getChars(NULL)) + dumpChars(chars, str->length()); + else + fprintf(stderr, "(oom in dumpString)"); } JS_FRIEND_API(void) js_DumpString(JSString *str) { - fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", - (void *) str, (void *) str->chars()); - dumpString(str); + if (const jschar *chars = str->getChars(NULL)) { + fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", + (void *) str, (void *) chars); + dumpString(str); + } else { + fprintf(stderr, "(oom in JS_DumpString)"); + } fputc('\n', stderr); } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index f12793f7eff3..42733418b153 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -986,17 +986,21 @@ struct JSObject : js::gc::Cell { static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3; static const uint32 QNAME_CLASS_RESERVED_SLOTS = 3; - inline jsval getNamePrefix() const; - inline void setNamePrefix(jsval prefix); + inline JSLinearString *getNamePrefix() const; + inline jsval getNamePrefixVal() const; + inline void setNamePrefix(JSLinearString *prefix); + inline void clearNamePrefix(); - inline jsval getNameURI() const; - inline void setNameURI(jsval uri); + inline JSLinearString *getNameURI() const; + inline jsval getNameURIVal() const; + inline void setNameURI(JSLinearString *uri); inline jsval getNamespaceDeclared() const; inline void setNamespaceDeclared(jsval decl); - inline jsval getQNameLocalName() const; - inline void setQNameLocalName(jsval decl); + inline JSLinearString *getQNameLocalName() const; + inline jsval getQNameLocalNameVal() const; + inline void setQNameLocalName(JSLinearString *name); /* * Proxy-specific getters and setters. diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index dab7223e4325..536242788ca0 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -530,32 +530,55 @@ JSObject::setNativeIterator(js::NativeIterator *ni) setPrivate(ni); } -inline jsval +inline JSLinearString * JSObject::getNamePrefix() const +{ + JS_ASSERT(isNamespace() || isQName()); + const js::Value &v = getSlot(JSSLOT_NAME_PREFIX); + return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL; +} + +inline jsval +JSObject::getNamePrefixVal() const { JS_ASSERT(isNamespace() || isQName()); return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX)); } inline void -JSObject::setNamePrefix(jsval prefix) +JSObject::setNamePrefix(JSLinearString *prefix) { JS_ASSERT(isNamespace() || isQName()); - setSlot(JSSLOT_NAME_PREFIX, js::Valueify(prefix)); + setSlot(JSSLOT_NAME_PREFIX, prefix ? js::StringValue(prefix) : js::UndefinedValue()); +} + +inline void +JSObject::clearNamePrefix() +{ + JS_ASSERT(isNamespace() || isQName()); + setSlot(JSSLOT_NAME_PREFIX, js::UndefinedValue()); +} + +inline JSLinearString * +JSObject::getNameURI() const +{ + JS_ASSERT(isNamespace() || isQName()); + const js::Value &v = getSlot(JSSLOT_NAME_URI); + return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL; } inline jsval -JSObject::getNameURI() const +JSObject::getNameURIVal() const { JS_ASSERT(isNamespace() || isQName()); return js::Jsvalify(getSlot(JSSLOT_NAME_URI)); } inline void -JSObject::setNameURI(jsval uri) +JSObject::setNameURI(JSLinearString *uri) { JS_ASSERT(isNamespace() || isQName()); - setSlot(JSSLOT_NAME_URI, js::Valueify(uri)); + setSlot(JSSLOT_NAME_URI, uri ? js::StringValue(uri) : js::UndefinedValue()); } inline jsval @@ -572,18 +595,26 @@ JSObject::setNamespaceDeclared(jsval decl) setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl)); } -inline jsval +inline JSLinearString * JSObject::getQNameLocalName() const +{ + JS_ASSERT(isQName()); + const js::Value &v = getSlot(JSSLOT_QNAME_LOCAL_NAME); + return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL; +} + +inline jsval +JSObject::getQNameLocalNameVal() const { JS_ASSERT(isQName()); return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME)); } inline void -JSObject::setQNameLocalName(jsval name) +JSObject::setQNameLocalName(JSLinearString *name) { JS_ASSERT(isQName()); - setSlot(JSSLOT_QNAME_LOCAL_NAME, js::Valueify(name)); + setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue()); } inline JSObject * diff --git a/js/src/json.cpp b/js/src/json.cpp index d81cadda309e..fe1566c4b8b7 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -119,12 +119,15 @@ js_json_parse(JSContext *cx, uintN argc, Value *vp) if (!JS_ConvertArguments(cx, argc, Jsvalify(argv), "S / v", &s, reviver.addr())) return JS_FALSE; + JSLinearString *linearStr = s->ensureLinear(cx); + if (!linearStr) + return JS_FALSE; + JSONParser *jp = js_BeginJSONParse(cx, vp); JSBool ok = jp != NULL; if (ok) { - const jschar *chars; - size_t length; - s->getCharsAndLength(chars, length); + const jschar *chars = linearStr->chars(); + size_t length = linearStr->length(); ok = js_ConsumeJSONText(cx, jp, chars, length); ok &= !!js_FinishJSONParse(cx, jp, reviver.value()); } @@ -403,9 +406,11 @@ JO(JSContext *cx, Value *vp, StringifyContext *scx) if (!s) return JS_FALSE; - const jschar *chars; - size_t length; - s->getCharsAndLength(chars, length); + size_t length = s->length(); + const jschar *chars = s->getChars(cx); + if (!chars) + return JS_FALSE; + if (!write_string(cx, scx->cb, chars, length) || !scx->cb.append(':') || !(scx->gap.empty() || scx->cb.append(' ')) || @@ -505,9 +510,11 @@ Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, Value *vp, } if (vp->isString()) { - const jschar *chars; - size_t length; - vp->toString()->getCharsAndLength(chars, length); + JSString *str = vp->toString(); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; return write_string(cx, scx->cb, chars, length); } diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 9fb4d383279e..b5279e6ef788 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -655,19 +655,16 @@ SprintCString(Sprinter *sp, const char *s) static ptrdiff_t SprintString(Sprinter *sp, JSString *str) { - const jschar *chars; - size_t length, size; - ptrdiff_t offset; + size_t length = str->length(); + const jschar *chars = str->getChars(sp->context); + if (!chars) + return -1; - str->getCharsAndLength(chars, length); - if (length == 0) - return sp->offset; - - size = js_GetDeflatedStringLength(sp->context, chars, length); + size_t size = js_GetDeflatedStringLength(sp->context, chars, length); if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size)) return -1; - offset = sp->offset; + ptrdiff_t offset = sp->offset; sp->offset += size; js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset, &size); @@ -713,39 +710,36 @@ const char js_EscapeMap[] = { static char * QuoteString(Sprinter *sp, JSString *str, uint32 quote) { - JSBool dontEscape, ok; - jschar qc, c; - ptrdiff_t off, len; - const jschar *s, *t, *z; - const char *e; - char *bp; - /* Sample off first for later return value pointer computation. */ - dontEscape = (quote & DONT_ESCAPE) != 0; - qc = (jschar) quote; - off = sp->offset; + JSBool dontEscape = (quote & DONT_ESCAPE) != 0; + jschar qc = (jschar) quote; + ptrdiff_t off = sp->offset; if (qc && Sprint(sp, "%c", (char)qc) < 0) return NULL; + const jschar *s = str->getChars(sp->context); + if (!s) + return NULL; + const jschar *z = s + str->length(); + /* Loop control variables: z points at end of string sentinel. */ - str->getCharsAndEnd(s, z); - for (t = s; t < z; s = ++t) { + for (const jschar *t = s; t < z; s = ++t) { /* Move t forward from s past un-quote-worthy characters. */ - c = *t; + jschar c = *t; while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' && !(c >> 8)) { c = *++t; if (t == z) break; } - len = t - s; + ptrdiff_t len = t - s; /* Allocate space for s, including the '\0' at the end. */ if (!SprintEnsureBuffer(sp, len)) return NULL; /* Advance sp->offset and copy s into sp's buffer. */ - bp = sp->base + sp->offset; + char *bp = sp->base + sp->offset; sp->offset += len; while (--len >= 0) *bp++ = (char) *s++; @@ -755,6 +749,8 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote) break; /* Use js_EscapeMap, \u, or \x only if necessary. */ + bool ok; + const char *e; if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) { ok = dontEscape ? Sprint(sp, "%c", (char)c) >= 0 @@ -1580,7 +1576,6 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) const char *lval; JSAtom *atom; jssrcnote *sn; - JSString *str; JSBool hole; LOCAL_ASSERT(*pc == JSOP_DUP); @@ -1661,18 +1656,19 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) case JSOP_GETPROP: LOAD_ATOM(0); do_destructure_atom: + { *OFF2STR(&ss->sprinter, head) = '{'; - str = ATOM_TO_STRING(atom); #if JS_HAS_DESTRUCTURING_SHORTHAND nameoff = ss->sprinter.offset; #endif - if (!QuoteString(&ss->sprinter, str, - js_IsIdentifier(str) ? 0 : (jschar)'\'')) { + if (!QuoteString(&ss->sprinter, atom, + js_IsIdentifier(atom) ? 0 : (jschar)'\'')) { return NULL; } if (SprintPut(&ss->sprinter, ": ", 2) < 0) return NULL; break; + } default: LOCAL_ASSERT(0); @@ -5198,7 +5194,11 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in, if (!fallback) return NULL; } - return js_DeflateString(cx, fallback->chars(), fallback->length()); + size_t length = fallback->length(); + const jschar *chars = fallback->getChars(cx); + if (!chars) + return NULL; + return js_DeflateString(cx, chars, length); } static char * diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 25118478dd5a..8345bef2ed63 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -8003,11 +8003,9 @@ Parser::xmlElementOrList(JSBool allowList) return NULL; } if (endAtom && startAtom && endAtom != startAtom) { - JSString *str = ATOM_TO_STRING(startAtom); - /* End vs. start tag name mismatch: point to the tag name. */ reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH, - str->chars()); + startAtom->chars()); return NULL; } @@ -8676,15 +8674,12 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) #if JS_HAS_XML_SUPPORT if (tokenStream.matchToken(TOK_DBLCOLON)) { if (afterDot) { - JSString *str; - /* * Here primaryExpr is called after . or .. followed by a name * followed by ::. This is the only case where a keyword after * . or .. is not treated as a property name. */ - str = ATOM_TO_STRING(pn->pn_atom); - tt = js_CheckKeyword(str->chars(), str->length()); + tt = js_CheckKeyword(pn->pn_atom->chars(), pn->pn_atom->length()); if (tt == TOK_FUNCTION) { pn->pn_arity = PN_NULLARY; pn->pn_type = TOK_FUNCTION; diff --git a/js/src/jspropertytree.cpp b/js/src/jspropertytree.cpp index 628942a40c4d..7c93ed8b86c3 100644 --- a/js/src/jspropertytree.cpp +++ b/js/src/jspropertytree.cpp @@ -480,13 +480,14 @@ Shape::dump(JSContext *cx, FILE *fp) const if (JSID_IS_INT(id)) { fprintf(fp, "[%ld]", (long) JSID_TO_INT(id)); } else { - JSString *str; + JSLinearString *str; if (JSID_IS_ATOM(id)) { - str = JSID_TO_STRING(id); + str = JSID_TO_ATOM(id); } else { JS_ASSERT(JSID_IS_OBJECT(id)); - str = js_ValueToString(cx, IdToValue(id)); + JSString *s = js_ValueToString(cx, IdToValue(id)); fputs("object ", fp); + str = s ? s->ensureLinear(cx) : NULL; } if (!str) fputs("", fp); diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index e5ca7f98601b..5a75de5ee22a 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -92,6 +92,7 @@ typedef struct JSTreeContext JSTreeContext; typedef struct JSTryNote JSTryNote; /* Friend "Advanced API" typedefs. */ +typedef struct JSLinearString JSLinearString; typedef struct JSAtom JSAtom; typedef struct JSAtomList JSAtomList; typedef struct JSAtomListElement JSAtomListElement; diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 5a42f874b3c9..31d80e1186dc 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -2829,7 +2829,12 @@ reflect_parse(JSContext *cx, uint32 argc, jsval *vp) if (!str) return JS_FALSE; - filename = js_DeflateString(cx, str->chars(), str->length()); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; + + filename = js_DeflateString(cx, chars, length); if (!filename) return JS_FALSE; filenamep.reset(filename); @@ -2844,10 +2849,10 @@ reflect_parse(JSContext *cx, uint32 argc, jsval *vp) } } - const jschar *chars; - size_t length; - - src->getCharsAndLength(chars, length); + size_t length = src->length(); + const jschar *chars = src->getChars(cx); + if (!chars) + return JS_FALSE; Parser parser(cx); diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index deda35feb885..e5546310e680 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -243,9 +243,11 @@ RegExp::handlePCREError(JSContext *cx, int error) bool RegExp::parseFlags(JSContext *cx, JSString *flagStr, uint32 &flagsOut) { - const jschar *s; - size_t n; - flagStr->getCharsAndLength(s, n); + size_t n = flagStr->length(); + const jschar *s = flagStr->getChars(cx); + if (!s) + return false; + flagsOut = 0; for (size_t i = 0; i < n; i++) { #define HANDLE_FLAG(__name) \ @@ -578,9 +580,12 @@ js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp) return true; } - const jschar *source; - size_t length; - re->getSource()->getCharsAndLength(source, length); + JSLinearString *src = re->getSource(); + size_t length = src->length(); + const jschar *source = src->getChars(cx); + if (!source) + return false; + if (length == 0) { source = empty_regexp_ucstr; length = JS_ARRAY_LENGTH(empty_regexp_ucstr) - 1; @@ -632,9 +637,11 @@ regexp_toString(JSContext *cx, uintN argc, Value *vp) static JSString * EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped) { - const jschar *oldChars; - size_t oldLen; - unescaped->getCharsAndLength(oldChars, oldLen); + size_t oldLen = unescaped->length(); + const jschar *oldChars = unescaped->getChars(cx); + if (!oldChars) + return NULL; + js::Vector newChars(cx); for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) { if (*it == '/' && (it == oldChars || it[-1] != '\\')) { diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 7f6fae024352..42e71145793f 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -61,7 +61,7 @@ class RegExpStatics typedef Vector MatchPairs; MatchPairs matchPairs; /* The input that was used to produce matchPairs. */ - JSString *matchPairsInput; + JSLinearString *matchPairsInput; /* The input last set on the statics. */ JSString *pendingInput; uintN flags; @@ -180,7 +180,7 @@ class RegExpStatics /* Mutators. */ - bool updateFromMatch(JSContext *cx, JSString *input, int *buf, size_t matchItemCount) { + bool updateFromMatch(JSContext *cx, JSLinearString *input, int *buf, size_t matchItemCount) { aboutToWrite(); pendingInput = input; diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 80cb83917fcd..371fead3e597 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -76,7 +76,7 @@ regexp_statics_construct(JSContext *cx, JSObject *parent) class RegExp { jsrefcount refCount; - JSString *source; + JSLinearString *source; #if ENABLE_YARR_JIT JSC::Yarr::RegexCodeBlock compiled; #else @@ -85,9 +85,9 @@ class RegExp unsigned parenCount; uint32 flags; - RegExp(JSString *source, uint32 flags) + RegExp(JSLinearString *source, uint32 flags) : refCount(1), source(source), compiled(), parenCount(0), flags(flags) {} - bool compileHelper(JSContext *cx, UString &pattern); + bool compileHelper(JSContext *cx, JSLinearString &pattern); bool compile(JSContext *cx); static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY; void handlePCREError(JSContext *cx, int error); @@ -154,7 +154,7 @@ class RegExp void decref(JSContext *cx); /* Accessors. */ - JSString *getSource() const { return source; } + JSLinearString *getSource() const { return source; } size_t getParenCount() const { return parenCount; } bool ignoreCase() const { return flags & JSREG_FOLD; } bool global() const { return flags & JSREG_GLOB; } @@ -274,7 +274,7 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC } inline bool -RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, +RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr, size_t *lastIndex, bool test, Value *rval) { #if !ENABLE_YARR_JIT @@ -299,8 +299,12 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, for (int *it = buf; it != buf + matchItemCount; ++it) *it = -1; - const jschar *chars = input->chars(); + JSLinearString *input = inputstr->ensureLinear(cx); + if (!input) + return false; + size_t len = input->length(); + const jschar *chars = input->chars(); /* * inputOffset emulates sticky mode by matching from this offset into the char buf and @@ -360,11 +364,14 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, inline RegExp * RegExp::create(JSContext *cx, JSString *source, uint32 flags) { + JSLinearString *flatSource = source->ensureLinear(cx); + if (!flatSource) + return NULL; RegExp *self; void *mem = cx->malloc(sizeof(*self)); if (!mem) return NULL; - self = new (mem) RegExp(source, flags); + self = new (mem) RegExp(flatSource, flags); if (!self->compile(cx)) { cx->destroy(self); return NULL; @@ -418,7 +425,7 @@ YarrJITIsBroken(JSContext *cx) #endif /* ANDROID */ inline bool -RegExp::compileHelper(JSContext *cx, UString &pattern) +RegExp::compileHelper(JSContext *cx, JSLinearString &pattern) { #if ENABLE_YARR_JIT bool fellBack = false; @@ -452,8 +459,13 @@ RegExp::compileHelper(JSContext *cx, UString &pattern) inline bool RegExp::compile(JSContext *cx) { + /* Flatten source early for the rest of compilation. */ + if (!source->ensureLinear(cx)) + return false; + if (!sticky()) return compileHelper(cx, *source); + /* * The sticky case we implement hackily by prepending a caret onto the front * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp. @@ -465,10 +477,10 @@ RegExp::compile(JSContext *cx) if (!cb.reserve(JS_ARRAY_LENGTH(prefix) + source->length() + JS_ARRAY_LENGTH(postfix))) return false; JS_ALWAYS_TRUE(cb.append(prefix, JS_ARRAY_LENGTH(prefix))); - JS_ALWAYS_TRUE(cb.append(source->chars(), source->length())); + JS_ALWAYS_TRUE(cb.append(source->flatChars(), source->length())); JS_ALWAYS_TRUE(cb.append(postfix, JS_ARRAY_LENGTH(postfix))); - JSString *fakeySource = js_NewStringFromCharBuffer(cx, cb); + JSLinearString *fakeySource = js_NewStringFromCharBuffer(cx, cb); if (!fakeySource) return false; return compileHelper(cx, *fakeySource); diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 6fa3087bd756..dfa8892e41f1 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -147,19 +147,17 @@ js_CheckKeyword(const jschar *str, size_t length) } JSBool -js_IsIdentifier(JSString *str) +js_IsIdentifier(JSLinearString *str) { - size_t length; - jschar c; - const jschar *chars, *end; + const jschar *chars = str->chars(); + size_t length = str->length(); - str->getCharsAndLength(chars, length); if (length == 0) return JS_FALSE; - c = *chars; + jschar c = *chars; if (!JS_ISIDSTART(c)) return JS_FALSE; - end = chars + length; + const jschar *end = chars + length; while (++chars != end) { c = *chars; if (!JS_ISIDENT(c)) diff --git a/js/src/jsscan.h b/js/src/jsscan.h index 44603f387ec6..09ea86c1a036 100644 --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -537,7 +537,7 @@ typedef void (*JSMapKeywordFun)(const char *); * check if str is a JS keyword. */ extern JSBool -js_IsIdentifier(JSString *str); +js_IsIdentifier(JSLinearString *str); /* * Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 278498c18332..46c33576a465 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1453,8 +1453,8 @@ PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize) name = trc->debugPrintIndex ? js_setter_str : js_getter_str; if (JSID_IS_ATOM(id)) { - n = PutEscapedString(buf, bufsize - 1, JSID_TO_STRING(id), 0); - if (n < bufsize - 1) + n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); + if (n < bufsize) JS_snprintf(buf + n, bufsize - n, " %s", name); } else if (JSID_IS_INT(shape->id)) { JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name); @@ -1476,8 +1476,8 @@ PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize) JS_ASSERT(!JSID_IS_VOID(id)); JS_ASSERT(JSID_IS_ATOM(id)); - n = PutEscapedString(buf, bufsize - 1, JSID_TO_STRING(id), 0); - if (n < bufsize - 1) + n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0); + if (n < bufsize) JS_snprintf(buf + n, bufsize - n, " method"); } #endif diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 5cd2e9821109..b7d10114adc3 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -112,8 +112,19 @@ RopeCapacityFor(size_t length) return RoundUpPow2(length); } -void -JSString::flatten() +static JS_ALWAYS_INLINE jschar * +AllocChars(JSContext *maybecx, size_t wholeCapacity) +{ + /* +1 for the null char at the end. */ + JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX); + size_t bytes = (wholeCapacity + 1) * sizeof(jschar); + if (maybecx) + return (jschar *)maybecx->malloc(bytes); + return (jschar *)js_malloc(bytes); +} + +const jschar * +JSString::flatten(JSContext *maybecx) { JS_ASSERT(isRope()); @@ -155,14 +166,16 @@ JSString::flatten() if (u.left->isExtensible() && u.left->s.capacity >= wholeLength) { wholeCapacity = u.left->s.capacity; - wholeChars = u.left->u.chars; + wholeChars = const_cast(u.left->u.chars); pos = wholeChars + u.left->length(); u.left->finishTraversalConversion(this, wholeChars, pos); goto visit_right_child; } wholeCapacity = RopeCapacityFor(wholeLength); - wholeChars = (jschar *)js_malloc((wholeCapacity + 1) * sizeof(jschar)); + wholeChars = AllocChars(maybecx, wholeCapacity); + if (!wholeCapacity) + return NULL; pos = wholeChars; first_visit_node: { JSString *left = str->u.left; /* Read before clobbered. */ @@ -194,7 +207,7 @@ JSString::flatten() JS_ASSERT(pos == wholeChars + wholeLength); *pos = '\0'; initFlatExtensible(wholeChars, wholeLength, wholeCapacity); - return; + return wholeChars; } size_t progress = str->lengthAndFlags; /* Read before clobbered. */ JSString *parent = str->s.parent; @@ -213,13 +226,12 @@ JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT #ifdef JS_TRACER -int32 JS_FASTCALL -js_Flatten(JSString* str) +JSBool JS_FASTCALL +js_Flatten(JSContext *cx, JSString* str) { - str->flatten(); - return 0; + return !!str->flatten(cx); } -JS_DEFINE_CALLINFO_1(extern, INT32, js_Flatten, STRING, 0, nanojit::ACCSET_STORE_ANY) +JS_DEFINE_CALLINFO_2(extern, BOOL, js_Flatten, CONTEXT, STRING, 0, nanojit::ACCSET_STORE_ANY) #endif /* !JS_TRACER */ @@ -240,10 +252,16 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) JSShortString *shortStr = js_NewGCShortString(cx); if (!shortStr) return NULL; + const jschar *leftChars = left->getChars(cx); + if (!leftChars) + return NULL; + const jschar *rightChars = right->getChars(cx); + if (!rightChars) + return NULL; jschar *buf = shortStr->init(wholeLength); - js_short_strncpy(buf, left->chars(), leftLen); - js_short_strncpy(buf + leftLen, right->chars(), rightLen); + js_short_strncpy(buf, leftChars, leftLen); + js_short_strncpy(buf + leftLen, rightChars, rightLen); buf[wholeLength] = 0; return shortStr->header(); } @@ -272,7 +290,8 @@ JSString::undepend(JSContext *cx) size_t n, size; jschar *s; - ensureNotRope(); + if (!ensureLinear(cx)) + return NULL; if (isDependent()) { n = dependentLength(); @@ -307,8 +326,7 @@ js_MakeStringImmutable(JSContext *cx, JSString *str) * Flattening a rope may result in a dependent string, so we need to flatten * before undepending the string. */ - str->ensureNotRope(); - if (!str->ensureNotDependent(cx)) { + if (!str->isFlat() && !str->undepend(cx)) { JS_RUNTIME_METER(cx->runtime, badUndependStrings); return JS_FALSE; } @@ -316,7 +334,7 @@ js_MakeStringImmutable(JSContext *cx, JSString *str) return JS_TRUE; } -static JSString * +static JSLinearString * ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg) { if (arg >= argc) @@ -326,9 +344,9 @@ ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg) if (vp->isObject() && !DefaultValue(cx, &vp->toObject(), JSTYPE_STRING, vp)) return NULL; - JSString *str; + JSLinearString *str; if (vp->isString()) { - str = vp->toString(); + str = vp->toString()->ensureLinear(cx); } else if (vp->isBoolean()) { str = ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[ (int)vp->toBoolean()]); @@ -338,9 +356,10 @@ ArgToRootedString(JSContext *cx, uintN argc, Value *vp, uintN arg) str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); } else { - str = js_NumberToString(cx, vp->toNumber()); - if (str) - vp->setString(str); + str = NumberToString(cx, vp->toNumber()); + if (!str) + return NULL; + vp->setString(str); } return str; } @@ -408,18 +427,12 @@ static const uint8 urlCharType[256] = JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval) { - JSString *str; - size_t i, ni, length, newlength; - const jschar *chars; - jschar *newchars; - jschar ch; - jsint mask; - jsdouble d; const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; + jsint mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; if (argc > 1) { + double d; if (!ValueToNumber(cx, argv[1], &d)) return JS_FALSE; if (!JSDOUBLE_IS_FINITE(d) || @@ -434,15 +447,17 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval } } - str = ArgToRootedString(cx, argc, argv - 2, 0); + JSLinearString *str = ArgToRootedString(cx, argc, argv - 2, 0); if (!str) return JS_FALSE; - str->getCharsAndLength(chars, length); - newlength = length; + size_t length = str->length(); + const jschar *chars = str->chars(); /* Take a first pass and see how big the result string will need to be. */ - for (i = 0; i < length; i++) { + size_t newlength = length; + for (size_t i = 0; i < length; i++) { + jschar ch; if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) continue; if (ch < 256) { @@ -468,10 +483,12 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval return JS_FALSE; } - newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar)); + jschar *newchars = (jschar *) cx->malloc((newlength + 1) * sizeof(jschar)); if (!newchars) return JS_FALSE; + size_t i, ni; for (i = 0, ni = 0; i < length; i++) { + jschar ch; if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { newchars[ni++] = ch; } else if (ch < 256) { @@ -494,12 +511,12 @@ js_str_escape(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval JS_ASSERT(ni == newlength); newchars[newlength] = 0; - str = js_NewString(cx, newchars, newlength); - if (!str) { + JSString *retstr = js_NewString(cx, newchars, newlength); + if (!retstr) { cx->free(newchars); return JS_FALSE; } - rval->setString(str); + rval->setString(retstr); return JS_TRUE; } #undef IS_OK @@ -515,25 +532,20 @@ str_escape(JSContext *cx, uintN argc, Value *vp) static JSBool str_unescape(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - size_t i, ni, length; - const jschar *chars; - jschar *newchars; - jschar ch; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) - return JS_FALSE; + return false; - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->chars(); /* Don't bother allocating less space for the new string. */ - newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); + jschar *newchars = (jschar *) cx->malloc((length + 1) * sizeof(jschar)); if (!newchars) - return JS_FALSE; - ni = i = 0; + return false; + size_t ni = 0, i = 0; while (i < length) { - ch = chars[i++]; + jschar ch = chars[i++]; if (ch == '%') { if (i + 1 < length && JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) @@ -555,12 +567,12 @@ str_unescape(JSContext *cx, uintN argc, Value *vp) } newchars[ni] = 0; - str = js_NewString(cx, newchars, ni); - if (!str) { + JSString *retstr = js_NewString(cx, newchars, ni); + if (!retstr) { cx->free(newchars); return JS_FALSE; } - vp->setString(str); + vp->setString(retstr); return JS_TRUE; } @@ -764,9 +776,10 @@ str_toSource(JSContext *cx, uintN argc, Value *vp) char buf[16]; size_t j = JS_snprintf(buf, sizeof buf, "(new String("); - const jschar *s; - size_t k; - str->getCharsAndLength(s, k); + size_t k = str->length(); + const jschar *s = str->getChars(cx); + if (!s) + return false; size_t n = j + k + 2; jschar *t = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); @@ -861,15 +874,15 @@ str_substring(JSContext *cx, uintN argc, Value *vp) JSString* JS_FASTCALL js_toLowerCase(JSContext *cx, JSString *str) { - size_t i, n; - const jschar *s; - jschar *news; + size_t n = str->length(); + const jschar *s = str->getChars(cx); + if (!s) + return NULL; - str->getCharsAndLength(s, n); - news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); if (!news) return NULL; - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) news[i] = JS_TOLOWER(s[i]); news[n] = 0; str = js_NewString(cx, news, n); @@ -912,15 +925,14 @@ str_toLocaleLowerCase(JSContext *cx, uintN argc, Value *vp) JSString* JS_FASTCALL js_toUpperCase(JSContext *cx, JSString *str) { - size_t i, n; - const jschar *s; - jschar *news; - - str->getCharsAndLength(s, n); - news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + size_t n = str->length(); + const jschar *s = str->getChars(cx); + if (!s) + return NULL; + jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); if (!news) return NULL; - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) news[i] = JS_TOUPPER(s[i]); news[n] = 0; str = js_NewString(cx, news, n); @@ -976,7 +988,10 @@ str_localeCompare(JSContext *cx, uintN argc, Value *vp) vp[2].setString(thatStr); return cx->localeCallbacks->localeCompare(cx, str, thatStr, Jsvalify(vp)); } - vp->setInt32(js_CompareStrings(str, thatStr)); + int32 result; + if (!CompareStrings(cx, str, thatStr, &result)) + return JS_FALSE; + vp->setInt32(result); } return JS_TRUE; } @@ -1025,8 +1040,6 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) { JSString *str; jsint i; - jsdouble d; - if (vp[1].isString() && argc != 0 && vp[2].isInt32()) { str = vp[1].toString(); i = vp[2].toInt32(); @@ -1035,11 +1048,12 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) } else { NORMALIZE_THIS(cx, vp, str); + double d; if (argc == 0) { d = 0.0; } else { if (!ValueToNumber(cx, vp[2], &d)) - return JS_FALSE; + return false; d = js_DoubleToInteger(d); } @@ -1048,12 +1062,17 @@ js_str_charCodeAt(JSContext *cx, uintN argc, Value *vp) i = (jsint) d; } - vp->setInt32(str->chars()[i]); - return JS_TRUE; + const jschar *chars; + chars = str->getChars(cx); + if (!chars) + return false; + + vp->setInt32(chars[i]); + return true; out_of_range: vp->setDouble(js_NaN); - return JS_TRUE; + return true; } jsint @@ -1255,7 +1274,10 @@ RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, js return false; while (!r.empty()) { if (threshold-- == 0 || !strs.append(r.front())) { - *match = StringMatch(textstr->chars(), textstrlen, pat, patlen); + const jschar *chars = textstr->getChars(cx); + if (!chars) + return false; + *match = StringMatch(chars, textstrlen, pat, patlen); return true; } if (!r.popFront()) @@ -1270,9 +1292,9 @@ RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, js for (JSString **outerp = strs.begin(); outerp != strs.end(); ++outerp) { /* First try to match without spanning two nodes. */ - const jschar *chars; - size_t len; - (*outerp)->getCharsAndLength(chars, len); + JSString *outer = *outerp; + const jschar *chars = outer->nonRopeChars(); + size_t len = outer->length(); jsint matchResult = StringMatch(chars, len, pat, patlen); if (matchResult != -1) { *match = pos + matchResult; @@ -1301,7 +1323,9 @@ RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, js *match = -1; return true; } - (*innerp)->getCharsAndEnd(tt, ttend); + JSString *inner = *innerp; + tt = inner->nonRopeChars(); + ttend = tt + inner->length(); } if (*pp != *tt) goto break_continue; @@ -1328,14 +1352,17 @@ str_indexOf(JSContext *cx, uintN argc, Value *vp) JSString *str; NORMALIZE_THIS(cx, vp, str); - JSString *patstr = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0); if (!patstr) - return JS_FALSE; + return false; - const jschar *text = str->chars(); jsuint textlen = str->length(); - const jschar *pat = patstr->chars(); + const jschar *text = str->getChars(cx); + if (!text) + return false; + jsuint patlen = patstr->length(); + const jschar *pat = patstr->chars(); jsuint start; if (argc > 1) { @@ -1379,41 +1406,37 @@ str_indexOf(JSContext *cx, uintN argc, Value *vp) static JSBool str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) { - JSString *str, *str2; - const jschar *text, *pat; - jsint i, j, textlen, patlen; - jsdouble d; + JSString *textstr; + NORMALIZE_THIS(cx, vp, textstr); + size_t textlen = textstr->length(); + const jschar *text = textstr->getChars(cx); + if (!text) + return false; - NORMALIZE_THIS(cx, vp, str); - text = str->chars(); - textlen = (jsint) str->length(); + JSLinearString *patstr = ArgToRootedString(cx, argc, vp, 0); + if (!patstr) + return false; - if (argc != 0 && vp[2].isString()) { - str2 = vp[2].toString(); - } else { - str2 = ArgToRootedString(cx, argc, vp, 0); - if (!str2) - return JS_FALSE; - } - pat = str2->chars(); - patlen = (jsint) str2->length(); + size_t patlen = patstr->length(); + const jschar *pat = patstr->chars(); - i = textlen - patlen; // Start searching here + jsint i = textlen - patlen; // Start searching here if (i < 0) { vp->setInt32(-1); - return JS_TRUE; + return true; } if (argc > 1) { if (vp[3].isInt32()) { - j = vp[3].toInt32(); + jsint j = vp[3].toInt32(); if (j <= 0) i = 0; else if (j < i) i = j; } else { + double d; if (!ValueToNumber(cx, vp[3], &d)) - return JS_FALSE; + return false; if (!JSDOUBLE_IS_NaN(d)) { d = js_DoubleToInteger(d); if (d <= 0) @@ -1426,7 +1449,7 @@ str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) if (patlen == 0) { vp->setInt32(i); - return JS_TRUE; + return true; } const jschar *t = text + i; @@ -1443,26 +1466,27 @@ str_lastIndexOf(JSContext *cx, uintN argc, Value *vp) goto break_continue; } vp->setInt32(t - text); - return JS_TRUE; + return true; } break_continue:; } vp->setInt32(-1); - return JS_TRUE; + return true; } static JSBool js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight) { JSString *str; - const jschar *chars; - size_t length, begin, end; - NORMALIZE_THIS(cx, vp, str); - str->getCharsAndLength(chars, length); - begin = 0; - end = length; + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return false; + + size_t begin = 0; + size_t end = length; if (trimLeft) { while (begin < length && JS_ISSPACE(chars[begin])) @@ -1476,10 +1500,10 @@ js_TrimString(JSContext *cx, Value *vp, JSBool trimLeft, JSBool trimRight) str = js_NewDependentString(cx, str, begin, end - begin); if (!str) - return JS_FALSE; + return false; vp->setString(str); - return JS_TRUE; + return true; } static JSBool @@ -1507,7 +1531,7 @@ str_trimRight(JSContext *cx, uintN argc, Value *vp) /* Result of a successfully performed flat match. */ class FlatMatch { - JSString *patstr; + JSLinearString *patstr; const jschar *pat; size_t patlen; int32 match_; @@ -1563,7 +1587,7 @@ class RegExpGuard */ static const size_t MAX_FLAT_PAT_LEN = 256; - static JSString *flattenPattern(JSContext *cx, JSString *patstr) { + static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) { JSCharBuffer cb(cx); if (!cb.reserve(patstr->length())) return NULL; @@ -1623,7 +1647,8 @@ class RegExpGuard if (rep.re_) return NULL; - fm.patstr->getCharsAndLength(fm.pat, fm.patlen); + fm.pat = fm.patstr->chars(); + fm.patlen = fm.patstr->length(); if (optarg < argc) return NULL; @@ -1641,9 +1666,8 @@ class RegExpGuard if (!RopeMatch(cx, textstr, fm.pat, fm.patlen, &fm.match_)) return NULL; } else { - const jschar *text; - size_t textlen; - textstr->getCharsAndLength(text, textlen); + const jschar *text = textstr->nonRopeChars(); + size_t textlen = textstr->length(); fm.match_ = StringMatch(text, textlen, fm.pat, fm.patlen); } return &fm; @@ -1863,9 +1887,9 @@ struct ReplaceData RegExpGuard g; /* regexp parameter object and private data */ JSObject *lambda; /* replacement function object or null */ JSObject *elembase; /* object for function(a){return b[a]} replace */ - JSString *repstr; /* replacement string */ - jschar *dollar; /* null or pointer to first $ in repstr */ - jschar *dollarEnd; /* limit pointer for js_strchr_limit */ + JSLinearString *repstr; /* replacement string */ + const jschar *dollar; /* null or pointer to first $ in repstr */ + const jschar *dollarEnd; /* limit pointer for js_strchr_limit */ jsint index; /* index in result of next replacement */ jsint leftIndex; /* left context index in str->chars */ JSSubString dollarStr; /* for "$$" InterpretDollar result */ @@ -1876,8 +1900,9 @@ struct ReplaceData }; static bool -InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, ReplaceData &rdata, - JSSubString *out, size_t *skip, volatile JSContext::DollarPath *path) +InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jschar *ep, + ReplaceData &rdata, JSSubString *out, size_t *skip, + volatile JSContext::DollarPath *path) { JS_ASSERT(*dp == '$'); @@ -1893,7 +1918,7 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, jschar *dp, jschar *ep, Repla if (num > res->parenCount()) return false; - jschar *cp = dp + 2; + const jschar *cp = dp + 2; if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { uintN tmp = 10 * num + JS7_UNDEC(dc); if (tmp <= res->parenCount()) { @@ -1994,7 +2019,9 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t if (shape->slot != SHAPE_INVALID_SLOT && shape->hasDefaultGetter()) { Value value = base->getSlot(shape->slot); if (value.isString()) { - rdata.repstr = value.toString(); + rdata.repstr = value.toString()->ensureLinear(cx); + if (!rdata.repstr) + return false; *sizep = rdata.repstr->length(); return true; } @@ -2050,10 +2077,12 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t return false; /* root repstr: rdata is on the stack, so scanned by conservative gc. */ - rdata.repstr = ValueToString_TestForStringInline(cx, session.rval()); + JSString *repstr = ValueToString_TestForStringInline(cx, session.rval()); + if (!repstr) + return false; + rdata.repstr = repstr->ensureLinear(cx); if (!rdata.repstr) return false; - *sizep = rdata.repstr->length(); return true; } @@ -2061,7 +2090,9 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t JSString *repstr = rdata.repstr; size_t replen = repstr->length(); JSContext::DollarPath path; - for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { + const jschar *dp = rdata.dollar; + const jschar *ep = rdata.dollarEnd; + for (; dp; dp = js_strchr_limit(dp, '$', ep)) { JSSubString sub; size_t skip; if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip, &path)) { @@ -2078,9 +2109,9 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t static void DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars) { - JSString *repstr = rdata.repstr; - jschar *cp; - jschar *bp = cp = repstr->chars(); + JSLinearString *repstr = rdata.repstr; + const jschar *cp; + const jschar *bp = cp = repstr->chars(); volatile JSContext::DollarPath path; #ifdef XP_WIN cx->dollarPath = &path; @@ -2088,7 +2119,9 @@ DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars) cx->blackBox = sourceBuf; #endif - for (jschar *dp = rdata.dollar, *ep = rdata.dollarEnd; dp; dp = js_strchr_limit(dp, '$', ep)) { + const jschar *dp = rdata.dollar; + const jschar *ep = rdata.dollarEnd; + for (; dp; dp = js_strchr_limit(dp, '$', ep)) { size_t len = dp - cp; js_strncpy(chars, cp, len); chars += len; @@ -2101,9 +2134,9 @@ DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars) if (((size_t(sub.chars) & 0xfffffU) + sub.length) > 0x100000U) { /* Going to cross a 0xffffe address, so take a gander at the replace value. */ volatile JSSubString vsub = sub; - volatile jschar *repstrChars = rdata.repstr->chars(); - volatile jschar *repstrDollar = rdata.dollar; - volatile jschar *repstrDollarEnd = rdata.dollarEnd; + volatile const jschar *repstrChars = rdata.repstr->chars(); + volatile const jschar *repstrDollar = rdata.dollar; + volatile const jschar *repstrDollarEnd = rdata.dollarEnd; cx->sub = &vsub; cx->repstrChars = &repstrChars; cx->repstrDollar = &repstrDollar; @@ -2129,12 +2162,12 @@ DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, jschar *chars) } static bool -ReplaceCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) +ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) { ReplaceData &rdata = *static_cast(p); rdata.calledBack = true; - JSString *str = rdata.str; + JSLinearString *str = rdata.str->assertIsLinear(); /* flattened for regexp */ size_t leftoff = rdata.leftIndex; const jschar *left = str->chars() + leftoff; size_t leftlen = res->matchStart() - leftoff; @@ -2240,13 +2273,16 @@ BuildFlatReplacement(JSContext *cx, JSString *textstr, JSString *repstr, * newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:] */ static inline bool -BuildDollarReplacement(JSContext *cx, JSString *textstr, JSString *repstr, +BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *repstr, const jschar *firstDollar, const FlatMatch &fm, Value *vp) { + JSLinearString *textstr = textstrArg->ensureLinear(cx); + if (!textstr) + return NULL; + JS_ASSERT(repstr->chars() <= firstDollar && firstDollar < repstr->chars() + repstr->length()); size_t matchStart = fm.match(); size_t matchLimit = matchStart + fm.patternLength(); - JSCharBuffer newReplaceChars(cx); /* * Most probably: @@ -2255,6 +2291,7 @@ BuildDollarReplacement(JSContext *cx, JSString *textstr, JSString *repstr, * * Note that dollar vars _could_ make the resulting text smaller than this. */ + JSCharBuffer newReplaceChars(cx); if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length())) return false; @@ -2325,7 +2362,7 @@ str_replace_regexp(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata) rdata.calledBack = false; RegExpStatics *res = cx->regExpStatics(); - if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceCallback, &rdata, REPLACE_ARGS)) + if (!DoMatch(cx, res, vp, rdata.str, *rep, ReplaceRegExpCallback, &rdata, REPLACE_ARGS)) return false; if (!rdata.calledBack) { @@ -2477,7 +2514,7 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp) const FlatMatch *fm = rdata.g.tryFlatMatch(cx, rdata.str, optarg, argc, false); if (!fm) { - if (cx->throwing) /* from tryFlatMatch */ + if (cx->throwing) /* oom in RopeMatch in tryFlatMatch */ return false; JS_ASSERT_IF(!rdata.g.hasRegExpPair(), argc > optarg); return str_replace_regexp(cx, argc, vp, rdata); @@ -2514,10 +2551,6 @@ static jsint find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsint *ip, JSSubString *sep) { - jsint i; - size_t length; - jschar *chars; - /* * Stop if past end of string. If at end of string, we will compare the * null char stored there (by js_NewString*) to sep->chars[j] in the while @@ -2529,12 +2562,14 @@ find_split(JSContext *cx, RegExpStatics *res, JSString *str, js::RegExp *re, jsi * However, we ape Perl and do this only if there is a sufficiently large * limit argument (see str_split). */ - i = *ip; - length = str->length(); + jsint i = *ip; + size_t length = str->length(); if ((size_t)i > length) return -1; - chars = str->chars(); + const jschar *chars = str->getChars(cx); + if (!chars) + return -2; /* * Match a regular expression against the separator at or above index i. @@ -2630,18 +2665,21 @@ str_split(JSContext *cx, uintN argc, Value *vp) sep->chars = NULL; sep->length = 0; } else { - JSString *str2 = js_ValueToString(cx, vp[2]); - if (!str2) + JSString *sepstr = js_ValueToString(cx, vp[2]); + if (!sepstr) return false; - vp[2].setString(str2); + vp[2].setString(sepstr); /* - * Point sep at a local copy of str2's header because find_split + * Point sep at a local copy of sepstr's header because find_split * will modify sep->length. */ - str2->getCharsAndLength(tmp.chars, tmp.length); - sep = &tmp; + tmp.length = sepstr->length(); + tmp.chars = sepstr->getChars(cx); + if (!tmp.chars) + return false; re = NULL; + sep = &tmp; } /* Use the second argument as the split limit, if given. */ @@ -2853,42 +2891,41 @@ str_slice(JSContext *cx, uintN argc, Value *vp) /* * HTML composition aids. */ -static JSBool -tagify(JSContext *cx, const char *begin, JSString *param, const char *end, +static bool +tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end, Value *vp) { - JSString *str; - jschar *tagbuf; - size_t beglen, endlen, parlen, taglen; - size_t i, j; - - NORMALIZE_THIS(cx, vp, str); + JSString *thisstr; + NORMALIZE_THIS(cx, vp, thisstr); + JSLinearString *str = thisstr->ensureLinear(cx); + if (!str) + return false; if (!end) end = begin; - beglen = strlen(begin); - taglen = 1 + beglen + 1; /* '' */ - parlen = 0; /* Avoid warning. */ + size_t beglen = strlen(begin); + size_t taglen = 1 + beglen + 1; /* '' */ + size_t parlen = 0; /* Avoid warning. */ if (param) { parlen = param->length(); taglen += 2 + parlen + 1; /* '="param"' */ } - endlen = strlen(end); - taglen += str->length() + 2 + endlen + 1; /* 'str' */ + size_t endlen = strlen(end); + taglen += str->length() + 2 + endlen + 1; /* 'str' */ if (taglen >= ~(size_t)0 / sizeof(jschar)) { js_ReportAllocationOverflow(cx); - return JS_FALSE; + return false; } - tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar)); + jschar *tagbuf = (jschar *) cx->malloc((taglen + 1) * sizeof(jschar)); if (!tagbuf) - return JS_FALSE; + return false; - j = 0; + size_t j = 0; tagbuf[j++] = '<'; - for (i = 0; i < beglen; i++) + for (size_t i = 0; i < beglen; i++) tagbuf[j++] = (jschar)begin[i]; if (param) { tagbuf[j++] = '='; @@ -2898,32 +2935,31 @@ tagify(JSContext *cx, const char *begin, JSString *param, const char *end, tagbuf[j++] = '"'; } tagbuf[j++] = '>'; + js_strncpy(&tagbuf[j], str->chars(), str->length()); j += str->length(); tagbuf[j++] = '<'; tagbuf[j++] = '/'; - for (i = 0; i < endlen; i++) + for (size_t i = 0; i < endlen; i++) tagbuf[j++] = (jschar)end[i]; tagbuf[j++] = '>'; JS_ASSERT(j == taglen); tagbuf[j] = 0; - str = js_NewString(cx, tagbuf, taglen); - if (!str) { + JSString *retstr = js_NewString(cx, tagbuf, taglen); + if (!retstr) { js_free((char *)tagbuf); - return JS_FALSE; + return false; } - vp->setString(str); - return JS_TRUE; + vp->setString(retstr); + return true; } static JSBool tagify_value(JSContext *cx, uintN argc, Value *vp, const char *begin, const char *end) { - JSString *param; - - param = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *param = ArgToRootedString(cx, argc, vp, 0); if (!param) return JS_FALSE; return tagify(cx, begin, param, end, vp); @@ -3366,7 +3402,7 @@ js_InitStringClass(JSContext *cx, JSObject *obj) return proto; } -JSString * +JSFlatString * js_NewString(JSContext *cx, jschar *chars, size_t length) { JSString *str; @@ -3400,10 +3436,10 @@ js_NewString(JSContext *cx, jschar *chars, size_t length) rt->lengthSquaredSum += (double)length * (double)length)); } #endif - return str; + return str->assertIsFlat(); } -static JS_ALWAYS_INLINE JSString * +static JS_ALWAYS_INLINE JSFlatString * NewShortString(JSContext *cx, const jschar *chars, size_t length) { JS_ASSERT(JSShortString::fitsIntoShortString(length)); @@ -3413,10 +3449,10 @@ NewShortString(JSContext *cx, const jschar *chars, size_t length) jschar *storage = str->init(length); js_short_strncpy(storage, chars, length); storage[length] = 0; - return str->header(); + return str->header()->assertIsFlat(); } -static JSString * +static JSFlatString * NewShortString(JSContext *cx, const char *chars, size_t length) { JS_ASSERT(JSShortString::fitsIntoShortString(length)); @@ -3438,15 +3474,15 @@ NewShortString(JSContext *cx, const char *chars, size_t length) size_t n = length; jschar *p = storage; while (n--) - *p++ = jschar(*chars++); + *p++ = (unsigned char)*chars++; *p = 0; } - return str->header(); + return str->header()->assertIsFlat(); } static const size_t sMinWasteSize = 16; -JSString * +JSFlatString * js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb) { if (cb.empty()) @@ -3479,14 +3515,14 @@ js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb) buf = tmp; } - JSString *str = js_NewString(cx, buf, length); + JSFlatString *str = js_NewString(cx, buf, length); if (!str) cx->free(buf); return str; } -JSString * -js_NewDependentString(JSContext *cx, JSString *base, size_t start, +JSLinearString * +js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t length) { JSString *ds; @@ -3494,12 +3530,16 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start, if (length == 0) return cx->runtime->emptyString; + JSLinearString *base = baseArg->ensureLinear(cx); + if (!base) + return NULL; + if (start == 0 && length == base->length()) return base; - jschar *chars = base->chars() + start; + const jschar *chars = base->chars() + start; - JSString *staticStr = JSString::lookupStaticString(chars, length); + JSLinearString *staticStr = JSString::lookupStaticString(chars, length); if (staticStr) return staticStr; @@ -3528,7 +3568,7 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start, rt->lengthSquaredSum += (double)length * (double)length)); } #endif - return ds; + return ds->assertIsLinear(); } #ifdef DEBUG @@ -3552,58 +3592,57 @@ void printJSStringStats(JSRuntime *rt) } #endif -JSString * +JSFlatString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n) { if (JSShortString::fitsIntoShortString(n)) return NewShortString(cx, s, n); - jschar *news; - JSString *str; - - news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); + jschar *news = (jschar *) cx->malloc((n + 1) * sizeof(jschar)); if (!news) return NULL; js_strncpy(news, s, n); news[n] = 0; - str = js_NewString(cx, news, n); + JSFlatString *str = js_NewString(cx, news, n); if (!str) cx->free(news); return str; } -JSString * +JSFlatString * js_NewStringCopyN(JSContext *cx, const char *s, size_t n) { if (JSShortString::fitsIntoShortString(n)) return NewShortString(cx, s, n); - return JS_NewStringCopyN(cx, s, n); + + jschar *chars = js_InflateString(cx, s, &n); + if (!chars) + return NULL; + JSFlatString *str = js_NewString(cx, chars, n); + if (!str) + cx->free(chars); + return str; } -JSString * +JSFlatString * js_NewStringCopyZ(JSContext *cx, const jschar *s) { - size_t n, m; - jschar *news; - JSString *str; - - n = js_strlen(s); - + size_t n = js_strlen(s); if (JSShortString::fitsIntoShortString(n)) return NewShortString(cx, s, n); - m = (n + 1) * sizeof(jschar); - news = (jschar *) cx->malloc(m); + size_t m = (n + 1) * sizeof(jschar); + jschar *news = (jschar *) cx->malloc(m); if (!news) return NULL; memcpy(news, s, m); - str = js_NewString(cx, news, n); + JSFlatString *str = js_NewString(cx, news, n); if (!str) cx->free(news); return str; } -JSString * +JSFlatString * js_NewStringCopyZ(JSContext *cx, const char *s) { return js_NewStringCopyN(cx, s, strlen(s)); @@ -3650,11 +3689,7 @@ js_ValueToString(JSContext *cx, const Value &arg) static inline JSBool AppendAtom(JSAtom *atom, JSCharBuffer &cb) { - JSString *str = ATOM_TO_STRING(atom); - const jschar *chars; - size_t length; - str->getCharsAndLength(chars, length); - return cb.append(chars, length); + return cb.append(atom->chars(), atom->length()); } /* This function implements E-262-3 section 9.8, toString. */ @@ -3663,12 +3698,14 @@ js_ValueToCharBuffer(JSContext *cx, const Value &arg, JSCharBuffer &cb) { Value v = arg; if (v.isObject() && !DefaultValue(cx, &v.toObject(), JSTYPE_STRING, &v)) - return JS_FALSE; + return false; if (v.isString()) { - const jschar *chars; - size_t length; - v.toString()->getCharsAndLength(chars, length); + JSString *str = v.toString(); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return false; return cb.append(chars, length); } if (v.isNumber()) @@ -3706,86 +3743,142 @@ js_ValueToSource(JSContext *cx, const Value &v) return js_ValueToString(cx, tvr.value()); } -/* - * str is not necessarily a GC thing here. - */ -uint32 -js_HashString(JSString *str) -{ - const jschar *s; - size_t n; - uint32 h; - - str->getCharsAndLength(s, n); - for (h = 0; n; s++, n--) - h = JS_ROTATE_LEFT32(h, 4) ^ *s; - return h; -} +namespace js { /* * str is not necessarily a GC thing here. */ -JSBool JS_FASTCALL -js_EqualStrings(JSString *str1, JSString *str2) +static JS_ALWAYS_INLINE bool +EqualStringsTail(JSLinearString *str1, size_t length1, JSLinearString *str2) { - size_t n; - const jschar *s1, *s2; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return JS_TRUE; - - n = str1->length(); - if (n != str2->length()) - return JS_FALSE; - - if (n == 0) - return JS_TRUE; - - s1 = str1->chars(), s2 = str2->chars(); + const jschar *s1 = str1->chars(); + const jschar *s1end = s1 + length1; + const jschar *s2 = str2->chars(); do { - if (*s1 != *s2) - return JS_FALSE; + if (*s1 != *s2) { + return false; + } ++s1, ++s2; - } while (--n != 0); + } while (s1 != s1end); - return JS_TRUE; + return true; } -JS_DEFINE_CALLINFO_2(extern, BOOL, js_EqualStrings, STRING, STRING, 1, nanojit::ACCSET_NONE) -int32 JS_FASTCALL -js_CompareStrings(JSString *str1, JSString *str2) +bool +EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result) { - size_t l1, l2, n, i; - const jschar *s1, *s2; - intN cmp; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return 0; - - str1->getCharsAndLength(s1, l1); - str2->getCharsAndLength(s2, l2); - n = JS_MIN(l1, l2); - for (i = 0; i < n; i++) { - cmp = s1[i] - s2[i]; - if (cmp != 0) - return cmp; + if (str1 == str2) { + *result = true; + return true; } - return (intN)(l1 - l2); + + size_t length1 = str1->length(); + if (length1 != str2->length()) { + *result = false; + return true; + } + + if (length1 == 0) { + *result = true; + return true; + } + + JSLinearString *linear1 = str1->ensureLinear(cx); + if (!linear1) + return false; + JSLinearString *linear2 = str2->ensureLinear(cx); + if (!linear2) + return false; + + *result = EqualStringsTail(linear1, length1, linear2); + return true; } -JS_DEFINE_CALLINFO_2(extern, INT32, js_CompareStrings, STRING, STRING, 1, nanojit::ACCSET_NONE) + +bool +EqualStrings(JSLinearString *str1, JSLinearString *str2) +{ + if (str1 == str2) + return true; + + size_t length1 = str1->length(); + if (length1 != str2->length()) + return false; + + if (length1 == 0) + return true; + + return EqualStringsTail(str1, length1, str2); +} + +} /* namespace js */ + +JSBool JS_FASTCALL +js_EqualStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2) +{ + JSBool result; + return EqualStrings(cx, str1, str2, &result) ? result : JS_NEITHER; +} +JS_DEFINE_CALLINFO_3(extern, BOOL, js_EqualStringsOnTrace, + CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE) namespace js { -JSBool -MatchStringAndAscii(JSString *str, const char *asciiBytes) +static bool +CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32 *result) +{ + JS_ASSERT(str1); + JS_ASSERT(str2); + + if (str1 == str2) { + *result = 0; + return true; + } + + size_t l1 = str1->length(); + const jschar *s1 = str1->getChars(cx); + if (!s1) + return false; + + size_t l2 = str2->length(); + const jschar *s2 = str2->getChars(cx); + if (!s2) + return false; + + size_t n = JS_MIN(l1, l2); + for (size_t i = 0; i < n; i++) { + if (int32 cmp = s1[i] - s2[i]) { + *result = cmp; + return true; + } + } + *result = (int32)(l1 - l2); + return true; +} + +bool +CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result) +{ + return CompareStringsImpl(cx, str1, str2, result); +} + +} /* namespace js */ + +int32 JS_FASTCALL +js_CompareStringsOnTrace(JSContext *cx, JSString *str1, JSString *str2) +{ + int32 result; + if (!CompareStringsImpl(cx, str1, str2, &result)) + return INT32_MIN; + JS_ASSERT(result != INT32_MIN); + return result; +} +JS_DEFINE_CALLINFO_3(extern, INT32, js_CompareStringsOnTrace, + CONTEXT, STRING, STRING, 1, nanojit::ACCSET_NONE) + +namespace js { + +bool +StringEqualsAscii(JSLinearString *str, const char *asciiBytes) { size_t length = strlen(asciiBytes); #ifdef DEBUG @@ -3804,8 +3897,6 @@ MatchStringAndAscii(JSString *str, const char *asciiBytes) } /* namespacejs */ - - size_t js_strlen(const jschar *s) { @@ -5511,26 +5602,24 @@ static JSBool Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, const jschar *unescapedSet2, Value *rval) { - size_t length, j, k, L; - JSCharBuffer cb(cx); - const jschar *chars; - jschar c, c2; - uint32 v; - uint8 utf8buf[4]; - jschar hexBuf[4]; static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ - str->getCharsAndLength(chars, length); + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; + if (length == 0) { rval->setString(cx->runtime->emptyString); return JS_TRUE; } - /* From this point the control must goto bad on failures. */ + JSCharBuffer cb(cx); + jschar hexBuf[4]; hexBuf[0] = '%'; hexBuf[3] = 0; - for (k = 0; k < length; k++) { - c = chars[k]; + for (size_t k = 0; k < length; k++) { + jschar c = chars[k]; if (js_strchr(unescapedSet, c) || (unescapedSet2 && js_strchr(unescapedSet2, c))) { if (!cb.append(c)) @@ -5541,6 +5630,7 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, JSMSG_BAD_URI, NULL); return JS_FALSE; } + uint32 v; if (c < 0xD800 || c > 0xDBFF) { v = c; } else { @@ -5550,7 +5640,7 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, JSMSG_BAD_URI, NULL); return JS_FALSE; } - c2 = chars[k]; + jschar c2 = chars[k]; if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI, NULL); @@ -5558,8 +5648,9 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, } v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; } - L = js_OneUcs4ToUtf8Char(utf8buf, v); - for (j = 0; j < L; j++) { + uint8 utf8buf[4]; + size_t L = js_OneUcs4ToUtf8Char(utf8buf, v); + for (size_t j = 0; j < L; j++) { hexBuf[1] = HexDigits[utf8buf[j] >> 4]; hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; if (!cb.append(hexBuf, 3)) @@ -5574,44 +5665,40 @@ Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, static JSBool Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) { - size_t length, start, k; - JSCharBuffer cb(cx); - const jschar *chars; - jschar c, H; - uint32 v; - jsuint B; - uint8 octets[4]; - intN j, n; + size_t length = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return JS_FALSE; - str->getCharsAndLength(chars, length); if (length == 0) { rval->setString(cx->runtime->emptyString); return JS_TRUE; } - /* From this point the control must goto bad on failures. */ - for (k = 0; k < length; k++) { - c = chars[k]; + JSCharBuffer cb(cx); + for (size_t k = 0; k < length; k++) { + jschar c = chars[k]; if (c == '%') { - start = k; + size_t start = k; if ((k + 2) >= length) goto report_bad_uri; if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) goto report_bad_uri; - B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + jsuint B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); k += 2; if (!(B & 0x80)) { c = (jschar)B; } else { - n = 1; + intN n = 1; while (B & (0x80 >> n)) n++; if (n == 1 || n > 4) goto report_bad_uri; + uint8 octets[4]; octets[0] = (uint8)B; if (k + 3 * (n - 1) >= length) goto report_bad_uri; - for (j = 1; j < n; j++) { + for (intN j = 1; j < n; j++) { k++; if (chars[k] != '%') goto report_bad_uri; @@ -5623,13 +5710,13 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) k += 2; octets[j] = (char)B; } - v = Utf8ToOneUcs4Char(octets, n); + uint32 v = Utf8ToOneUcs4Char(octets, n); if (v >= 0x10000) { v -= 0x10000; if (v > 0xFFFFF) goto report_bad_uri; c = (jschar)((v & 0x3FF) + 0xDC00); - H = (jschar)((v >> 10) + 0xD800); + jschar H = (jschar)((v >> 10) + 0xD800); if (!cb.append(H)) return JS_FALSE; } else { @@ -5661,9 +5748,7 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) static JSBool str_decodeURI(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Decode(cx, str, js_uriReservedPlusPound_ucstr, vp); @@ -5672,9 +5757,7 @@ str_decodeURI(JSContext *cx, uintN argc, Value *vp) static JSBool str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Decode(cx, str, js_empty_ucstr, vp); @@ -5683,9 +5766,7 @@ str_decodeURI_Component(JSContext *cx, uintN argc, Value *vp) static JSBool str_encodeURI(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, @@ -5695,9 +5776,7 @@ str_encodeURI(JSContext *cx, uintN argc, Value *vp) static JSBool str_encodeURI_Component(JSContext *cx, uintN argc, Value *vp) { - JSString *str; - - str = ArgToRootedString(cx, argc, vp, 0); + JSLinearString *str = ArgToRootedString(cx, argc, vp, 0); if (!str) return JS_FALSE; return Encode(cx, str, js_uriUnescaped_ucstr, NULL, vp); @@ -5773,13 +5852,8 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) namespace js { size_t -PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, uint32 quote) +PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSLinearString *str, uint32 quote) { - const jschar *chars, *charsEnd; - size_t n; - const char *escape; - char c; - uintN u, hex, shift; enum { STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE } state; @@ -5793,13 +5867,14 @@ PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, u else bufferSize--; - str->getCharsAndEnd(chars, charsEnd); - n = 0; + const jschar *chars = str->chars(); + const jschar *charsEnd = chars + str->length(); + size_t n = 0; state = FIRST_QUOTE; - shift = 0; - hex = 0; - u = 0; - c = 0; /* to quell GCC warnings */ + uintN shift = 0; + uintN hex = 0; + uintN u = 0; + char c = 0; /* to quell GCC warnings */ for (;;) { switch (state) { @@ -5823,7 +5898,7 @@ PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, u u = *chars++; if (u < ' ') { if (u != 0) { - escape = strchr(js_EscapeMap, (int)u); + const char *escape = strchr(js_EscapeMap, (int)u); if (escape) { u = escape[1]; goto do_escape; diff --git a/js/src/jsstr.h b/js/src/jsstr.h index ddc24078bb90..b4db79100244 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -83,6 +83,8 @@ namespace js { namespace mjit { class Compiler; }} +struct JSLinearString; + /* * The GC-thing "string" type. * @@ -110,6 +112,10 @@ namespace js { namespace mjit { * * When a string is a ROPE, it represents the lazy concatenation of other * strings. In general, the nodes reachable from any rope form a dag. + * + * To allow static type-based checking that a given JSString* always points + * to a flat or non-rope string, the JSFlatString and JSLinearString types may + * be used. Instead of casting, callers should use ensureX() and assertIsX(). */ struct JSString { @@ -124,7 +130,7 @@ struct JSString */ size_t lengthAndFlags; /* in all strings */ union { - jschar *chars; /* in non-rope strings */ + const jschar *chars; /* in non-rope strings */ JSString *left; /* in rope strings */ } u; union { @@ -211,11 +217,6 @@ struct JSString return lengthAndFlags & ROPE_BIT; } - JS_ALWAYS_INLINE jschar *chars() { - ensureNotRope(); - return u.chars; - } - JS_ALWAYS_INLINE size_t length() const { return lengthAndFlags >> LENGTH_SHIFT; } @@ -224,13 +225,18 @@ struct JSString return lengthAndFlags <= TYPE_FLAGS_MASK; } - JS_ALWAYS_INLINE void getCharsAndLength(const jschar *&chars, size_t &length) { - chars = this->chars(); - length = this->length(); + /* This can fail by returning null and reporting an error on cx. */ + JS_ALWAYS_INLINE const jschar *getChars(JSContext *cx) { + if (isRope()) + return flatten(cx); + return nonRopeChars(); } - JS_ALWAYS_INLINE void getCharsAndEnd(const jschar *&chars, const jschar *&end) { - end = length() + (chars = this->chars()); + /* This can fail by returning null and reporting an error on cx. */ + JS_ALWAYS_INLINE const jschar *getCharsZ(JSContext *cx) { + if (!isFlat()) + return undepend(cx); + return flatChars(); } JS_ALWAYS_INLINE void initFlatNotTerminated(jschar *chars, size_t length) { @@ -246,7 +252,7 @@ struct JSString JS_ASSERT(chars[length] == jschar(0)); } - JS_ALWAYS_INLINE void initShortString(jschar *chars, size_t length) { + JS_ALWAYS_INLINE void initShortString(const jschar *chars, size_t length) { JS_ASSERT(length <= MAX_LENGTH); JS_ASSERT(chars >= inlineStorage && chars < (jschar *)(this + 2)); JS_ASSERT(!isStatic(this)); @@ -263,7 +269,12 @@ struct JSString s.capacity = cap; } - JS_ALWAYS_INLINE jschar *flatChars() const { + JS_ALWAYS_INLINE JSFlatString *assertIsFlat() { + JS_ASSERT(isFlat()); + return reinterpret_cast(this); + } + + JS_ALWAYS_INLINE const jschar *flatChars() const { JS_ASSERT(isFlat()); return u.chars; } @@ -293,22 +304,22 @@ struct JSString * The chars pointer should point somewhere inside the buffer owned by base. * The caller still needs to pass base for GC purposes. */ - inline void initDependent(JSString *base, jschar *chars, size_t length) { + inline void initDependent(JSString *base, const jschar *chars, size_t length) { JS_ASSERT(!isStatic(this)); JS_ASSERT(base->isFlat()); - JS_ASSERT(chars >= base->chars() && chars < base->chars() + base->length()); - JS_ASSERT(length <= base->length() - (chars - base->chars())); + JS_ASSERT(chars >= base->flatChars() && chars < base->flatChars() + base->length()); + JS_ASSERT(length <= base->length() - (chars - base->flatChars())); lengthAndFlags = buildLengthAndFlags(length, DEPENDENT); u.chars = chars; s.base = base; } - inline JSString *dependentBase() const { + inline JSLinearString *dependentBase() const { JS_ASSERT(isDependent()); - return s.base; + return s.base->assertIsLinear(); } - JS_ALWAYS_INLINE jschar *dependentChars() { + JS_ALWAYS_INLINE const jschar *dependentChars() { JS_ASSERT(isDependent()); return u.chars; } @@ -320,16 +331,6 @@ struct JSString const jschar *undepend(JSContext *cx); - const jschar *getCharsZ(JSContext *cx) { - if (!isFlat()) - return undepend(cx); - return flatChars(); - } - - inline bool ensureNotDependent(JSContext *cx) { - return !isDependent() || undepend(cx); - } - const jschar *nonRopeChars() const { JS_ASSERT(!isRope()); return u.chars; @@ -353,17 +354,27 @@ struct JSString return s.right; } - inline void finishTraversalConversion(JSString *base, jschar *baseBegin, jschar *end) { + inline void finishTraversalConversion(JSString *base, const jschar *baseBegin, const jschar *end) { JS_ASSERT(baseBegin <= u.chars && u.chars <= end); lengthAndFlags = buildLengthAndFlags(end - u.chars, DEPENDENT); s.base = base; } - void flatten(); + const jschar *flatten(JSContext *maybecx); - void ensureNotRope() { - if (isRope()) - flatten(); + JSLinearString *ensureLinear(JSContext *cx) { + if (isRope() && !flatten(cx)) + return NULL; + return reinterpret_cast(this); + } + + bool isLinear() const { + return !isRope(); + } + + JSLinearString *assertIsLinear() { + JS_ASSERT(isLinear()); + return reinterpret_cast(this); } typedef uint8 SmallChar; @@ -426,13 +437,13 @@ struct JSString */ static const JSString *const intStringTable[]; - static JSString *unitString(jschar c); - static JSString *getUnitString(JSContext *cx, JSString *str, size_t index); - static JSString *length2String(jschar c1, jschar c2); - static JSString *length2String(uint32 i); - static JSString *intString(jsint i); + static JSFlatString *unitString(jschar c); + static JSLinearString *getUnitString(JSContext *cx, JSString *str, size_t index); + static JSFlatString *length2String(jschar c1, jschar c2); + static JSFlatString *length2String(uint32 i); + static JSFlatString *intString(jsint i); - static JSString *lookupStaticString(const jschar *chars, size_t length); + static JSFlatString *lookupStaticString(const jschar *chars, size_t length); JS_ALWAYS_INLINE void finalize(JSContext *cx); @@ -450,12 +461,37 @@ struct JSString } }; -struct JSFlatString : JSString +/* + * A "linear" string may or may not be null-terminated, but it provides + * infallible access to a linear array of characters. Namely, this means the + * string is not a rope. + */ +struct JSLinearString : JSString { + const jschar *chars() const { return JSString::nonRopeChars(); } +}; + +JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString)); + +/* + * A linear string where, additionally, chars()[length()] == '\0'. Namely, this + * means the string is not a dependent string or rope. + */ +struct JSFlatString : JSLinearString +{ + const jschar *charsZ() const { return chars(); } }; JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString)); +/* + * A flat string which has been "atomized", i.e., that is a unique string among + * other atomized strings and therefore allows equality via pointer comparison. + */ +struct JSAtom : JSFlatString +{ +}; + struct JSExternalString : JSString { static const uintN TYPE_LIMIT = 8; @@ -548,6 +584,7 @@ namespace js { * Implemented in jsstrinlines.h. */ class StringSegmentRange; +class MutatingRopeSegmentRange; /* * Utility for building a rope (lazy concatenation) of strings. @@ -747,7 +784,7 @@ extern const char js_decodeURIComponent_str[]; extern const char js_encodeURIComponent_str[]; /* GC-allocate a string descriptor for the given malloc-allocated chars. */ -extern JSString * +extern JSFlatString * js_NewString(JSContext *cx, jschar *chars, size_t length); /* @@ -755,25 +792,25 @@ js_NewString(JSContext *cx, jschar *chars, size_t length); * This function takes responsibility for adding the terminating '\0' required * by js_NewString. */ -extern JSString * +extern JSFlatString * js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb); -extern JSString * +extern JSLinearString * js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length); /* Copy a counted string and GC-allocate a descriptor for it. */ -extern JSString * +extern JSFlatString * js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n); -extern JSString * +extern JSFlatString * js_NewStringCopyN(JSContext *cx, const char *s, size_t n); /* Copy a C string and GC-allocate a descriptor for it. */ -extern JSString * +extern JSFlatString * js_NewStringCopyZ(JSContext *cx, const jschar *s); -extern JSString * +extern JSFlatString * js_NewStringCopyZ(JSContext *cx, const char *s); /* @@ -826,29 +863,42 @@ js_ValueToSource(JSContext *cx, const js::Value &v); * Compute a hash function from str. The caller can call this function even if * str is not a GC-allocated thing. */ -extern uint32 -js_HashString(JSString *str); +inline uint32 +js_HashString(JSLinearString *str) +{ + const jschar *s = str->chars(); + size_t n = str->length(); + uint32 h; + for (h = 0; n; s++, n--) + h = JS_ROTATE_LEFT32(h, 4) ^ *s; + return h; +} + +namespace js { /* * Test if strings are equal. The caller can call the function even if str1 * or str2 are not GC-allocated things. */ -extern JSBool JS_FASTCALL -js_EqualStrings(JSString *str1, JSString *str2); +extern bool +EqualStrings(JSContext *cx, JSString *str1, JSString *str2, JSBool *result); + +/* EqualStrings is infallible on linear strings. */ +extern bool +EqualStrings(JSLinearString *str1, JSLinearString *str2); /* * Return less than, equal to, or greater than zero depending on whether * str1 is less than, equal to, or greater than str2. */ -extern int32 JS_FASTCALL -js_CompareStrings(JSString *str1, JSString *str2); +extern bool +CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result); -namespace js { /* * Return true if the string matches the given sequence of ASCII bytes. */ -extern JSBool -MatchStringAndAscii(JSString *str, const char *asciiBytes); +extern bool +StringEqualsAscii(JSLinearString *str, const char *asciiBytes); } /* namespacejs */ @@ -994,7 +1044,7 @@ js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); namespace js { extern size_t -PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSString *str, uint32 quote); +PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSLinearString *str, uint32 quote); /* * Write str into buffer escaping any non-printable or non-ASCII character @@ -1006,7 +1056,7 @@ PutEscapedStringImpl(char *buffer, size_t size, FILE *fp, JSString *str, uint32 * be a single or double quote character that will quote the output. */ inline size_t -PutEscapedString(char *buffer, size_t size, JSString *str, uint32 quote) +PutEscapedString(char *buffer, size_t size, JSLinearString *str, uint32 quote) { size_t n = PutEscapedStringImpl(buffer, size, NULL, str, quote); @@ -1021,7 +1071,7 @@ PutEscapedString(char *buffer, size_t size, JSString *str, uint32 quote) * will quote the output. */ inline bool -FileEscapedString(FILE *fp, JSString *str, uint32 quote) +FileEscapedString(FILE *fp, JSLinearString *str, uint32 quote) { return PutEscapedStringImpl(NULL, 0, fp, str, quote) != size_t(-1); } diff --git a/js/src/jsstrinlines.h b/js/src/jsstrinlines.h index 804dab1772b7..39f9912c986c 100644 --- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -42,49 +42,53 @@ #include "jsstr.h" -inline JSString * +inline JSFlatString * JSString::unitString(jschar c) { JS_ASSERT(c < UNIT_STRING_LIMIT); - return const_cast(&unitStringTable[c]); + return const_cast(&unitStringTable[c])->assertIsFlat(); } -inline JSString * +inline JSLinearString * JSString::getUnitString(JSContext *cx, JSString *str, size_t index) { JS_ASSERT(index < str->length()); - jschar c = str->chars()[index]; + const jschar *chars = str->getChars(cx); + if (!chars) + return NULL; + jschar c = chars[index]; if (c < UNIT_STRING_LIMIT) return unitString(c); return js_NewDependentString(cx, str, index, 1); } -inline JSString * +inline JSFlatString * JSString::length2String(jschar c1, jschar c2) { JS_ASSERT(fitsInSmallChar(c1)); JS_ASSERT(fitsInSmallChar(c2)); - return const_cast - (&length2StringTable[(((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]]); + return const_cast ( + &length2StringTable[(((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]] + )->assertIsFlat(); } -inline JSString * +inline JSFlatString * JSString::length2String(uint32 i) { JS_ASSERT(i < 100); return length2String('0' + i / 10, '0' + i % 10); } -inline JSString * +inline JSFlatString * JSString::intString(jsint i) { jsuint u = jsuint(i); JS_ASSERT(u < INT_STRING_LIMIT); - return const_cast(JSString::intStringTable[u]); + return const_cast(JSString::intStringTable[u])->assertIsFlat(); } /* Get a static atomized string for chars if possible. */ -inline JSString * +inline JSFlatString * JSString::lookupStaticString(const jschar *chars, size_t length) { if (length == 1) { @@ -125,14 +129,13 @@ JSString::finalize(JSContext *cx) { JS_ASSERT(!JSString::isStatic(this)); JS_RUNTIME_UNMETER(cx->runtime, liveStrings); if (isDependent()) { - JS_ASSERT(dependentBase()); JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings); } else if (isFlat()) { /* * flatChars for stillborn string is null, but cx->free checks * for a null pointer on its own. */ - cx->free(flatChars()); + cx->free(const_cast(flatChars())); } } @@ -153,7 +156,7 @@ JSExternalString::finalize(JSContext *cx) JS_RUNTIME_UNMETER(cx->runtime, liveStrings); /* A stillborn string has null chars. */ - jschar *chars = flatChars(); + jschar *chars = const_cast(flatChars()); if (!chars) return; JSStringFinalizeOp finalizer = str_finalizers[externalStringType]; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index cc45c1f4e08a..d8c09108e38f 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -437,14 +437,14 @@ jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) int index = -1; if (JSID_IS_STRING(id)) { - JSString* str = JSID_TO_STRING(id); - if (MatchStringAndAscii(str, "HOTLOOP")) { + JSAtom* str = JSID_TO_ATOM(id); + if (StringEqualsAscii(str, "HOTLOOP")) { *vp = INT_TO_JSVAL(HOTLOOP); return JS_TRUE; } #ifdef JS_METHODJIT - if (MatchStringAndAscii(str, "profiler")) { + if (StringEqualsAscii(str, "profiler")) { *vp = BOOLEAN_TO_JSVAL(cx->profilingEnabled); return JS_TRUE; } @@ -910,7 +910,11 @@ PrintOnTrace(char* format, uint32 argc, double *argv) // protect against massive spew if u.s is a bad pointer. if (length > 1 << 16) length = 1 << 16; - jschar *chars = u.s->chars(); + if (u.s->isRope()) { + fprintf(out, ""); + break; + } + const jschar *chars = u.s->nonRopeChars(); for (unsigned i = 0; i < length; ++i) { jschar co = chars[i]; if (co < 128) @@ -8429,8 +8433,13 @@ TraceRecorder::d2i(LIns* d, bool resultCanBeImpreciseIfFractional) #endif } if (ci == &js_StringToNumber_ci) { - LIns* args[] = { d->callArgN(1), d->callArgN(0) }; - return w.call(&js_StringToInt32_ci, args); + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, d->callArgN(1), d->callArgN(0) }; + LIns* ret_ins = w.call(&js_StringToInt32_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + return ret_ins; } } return resultCanBeImpreciseIfFractional @@ -8646,10 +8655,13 @@ TraceRecorder::switchop() w.name(w.eqd(v_ins, w.immd(d)), "guard(switch on numeric)"), BRANCH_EXIT); } else if (v.isString()) { - LIns* args[] = { w.immpStrGC(v.toString()), v_ins }; - guard(true, - w.name(w.eqi0(w.eqi0(w.call(&js_EqualStrings_ci, args))), - "guard(switch on string)"), + LIns* args[] = { w.immpStrGC(v.toString()), v_ins, cx_ins }; + LIns* equal_rval = w.call(&js_EqualStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(equal_rval, JS_NEITHER), "guard(oom)"), + OOM_EXIT); + guard(false, + w.name(w.eqi0(equal_rval), "guard(switch on string)"), BRANCH_EXIT); } else if (v.isBoolean()) { guard(true, @@ -8711,8 +8723,12 @@ TraceRecorder::incHelper(const Value &v, LIns*& v_ins, LIns*& v_after, jsint inc if (v.isBoolean()) { v_ins = w.i2d(v_ins); } else if (v.isString()) { - LIns* args[] = { v_ins, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, v_ins, cx_ins }; v_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else { JS_ASSERT(v.isNumber()); } @@ -8804,14 +8820,18 @@ EvalCmp(LOpcode op, double l, double r) } static bool -EvalCmp(LOpcode op, JSString* l, JSString* r) +EvalCmp(JSContext *cx, LOpcode op, JSString* l, JSString* r, JSBool *ret) { if (op == LIR_eqd) - return !!js_EqualStrings(l, r); - return EvalCmp(op, js_CompareStrings(l, r), 0); + return EqualStrings(cx, l, r, ret); + JSBool cmp; + if (!CompareStrings(cx, l, r, &cmp)) + return false; + *ret = EvalCmp(op, cmp, 0); + return true; } -JS_REQUIRES_STACK void +JS_REQUIRES_STACK RecordingStatus TraceRecorder::strictEquality(bool equal, bool cmpCase) { Value& r = stackval(-1); @@ -8819,16 +8839,21 @@ TraceRecorder::strictEquality(bool equal, bool cmpCase) LIns* l_ins = get(&l); LIns* r_ins = get(&r); LIns* x; - bool cond; + JSBool cond; JSValueType ltag = getPromotedType(l); if (ltag != getPromotedType(r)) { cond = !equal; x = w.immi(cond); } else if (ltag == JSVAL_TYPE_STRING) { - LIns* args[] = { r_ins, l_ins }; - x = w.eqiN(w.call(&js_EqualStrings_ci, args), equal); - cond = !!js_EqualStrings(l.toString(), r.toString()); + LIns* args[] = { r_ins, l_ins, cx_ins }; + LIns* equal_ins = w.call(&js_EqualStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(equal_ins, JS_NEITHER), "guard(oom)"), + OOM_EXIT); + x = w.eqiN(equal_ins, equal); + if (!EqualStrings(cx, l.toString(), r.toString(), &cond)) + RETURN_ERROR("oom"); } else { if (ltag == JSVAL_TYPE_DOUBLE) x = w.eqd(l_ins, r_ins); @@ -8848,10 +8873,11 @@ TraceRecorder::strictEquality(bool equal, bool cmpCase) /* Only guard if the same path may not always be taken. */ if (!x->isImmI()) guard(cond, x, BRANCH_EXIT); - return; + return RECORD_CONTINUE; } set(&l, x); + return RECORD_CONTINUE; } JS_REQUIRES_STACK AbortableRecordingStatus @@ -8871,8 +8897,8 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins, Value& rval) { LOpcode op = LIR_eqi; - bool cond; - LIns* args[] = { NULL, NULL }; + JSBool cond; + LIns* args[] = { NULL, NULL, NULL }; /* * The if chain below closely mirrors that found in 11.9.3, in general @@ -8913,11 +8939,16 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins, l_ins = w.getStringChar(l_ins, w.immpNonGC(0)); r_ins = w.getStringChar(r_ins, w.immpNonGC(0)); } else { - args[0] = r_ins, args[1] = l_ins; - l_ins = w.call(&js_EqualStrings_ci, args); + args[0] = r_ins, args[1] = l_ins, args[2] = cx_ins; + LIns *equal_ins = w.call(&js_EqualStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(equal_ins, JS_NEITHER), "guard(oom)"), + OOM_EXIT); + l_ins = equal_ins; r_ins = w.immi(1); } - cond = !!js_EqualStrings(l.toString(), r.toString()); + if (!EqualStrings(cx, l.toString(), r.toString(), &cond)) + RETURN_ERROR_A("oom"); } else { JS_ASSERT(l.isNumber() && r.isNumber()); cond = (l.toNumber() == r.toNumber()); @@ -8930,14 +8961,30 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins, r_ins = w.immiUndefined(); cond = true; } else if (l.isNumber() && r.isString()) { - args[0] = r_ins, args[1] = cx_ins; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + args[0] = ok_ins, args[1] = r_ins, args[2] = cx_ins; r_ins = w.call(&js_StringToNumber_ci, args); - cond = (l.toNumber() == js_StringToNumber(cx, r.toString())); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + double d = js_StringToNumber(cx, r.toString(), &ok); + if (!ok) + RETURN_ERROR_A("oom"); + cond = (l.toNumber() == d); op = LIR_eqd; } else if (l.isString() && r.isNumber()) { - args[0] = l_ins, args[1] = cx_ins; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + args[0] = ok_ins, args[1] = l_ins, args[2] = cx_ins; l_ins = w.call(&js_StringToNumber_ci, args); - cond = (js_StringToNumber(cx, l.toString()) == r.toNumber()); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + double d = js_StringToNumber(cx, l.toString(), &ok); + if (!ok) + RETURN_ERROR_A("oom"); + cond = (d == r.toNumber()); op = LIR_eqd; } else { // Below we may assign to l or r, which modifies the interpreter state. @@ -9011,7 +9058,7 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) Value& r = stackval(-1); Value& l = stackval(-2); LIns* x = NULL; - bool cond; + JSBool cond; LIns* l_ins = get(&l); LIns* r_ins = get(&r); bool fp = false; @@ -9037,22 +9084,31 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) /* 11.8.5 steps 3, 16-21. */ if (l.isString() && r.isString()) { - LIns* args[] = { r_ins, l_ins }; - l_ins = w.call(&js_CompareStrings_ci, args); + LIns* args[] = { r_ins, l_ins, cx_ins }; + LIns* result_ins = w.call(&js_CompareStringsOnTrace_ci, args); + guard(false, + w.name(w.eqiN(result_ins, INT32_MIN), "guard(oom)"), + OOM_EXIT); + l_ins = result_ins; r_ins = w.immi(0); - cond = EvalCmp(op, l.toString(), r.toString()); + if (!EvalCmp(cx, op, l.toString(), r.toString(), &cond)) + RETURN_ERROR_A("oom"); goto do_comparison; } /* 11.8.5 steps 4-5. */ if (!l.isNumber()) { - LIns* args[] = { l_ins, cx_ins }; if (l.isBoolean()) { l_ins = w.i2d(l_ins); } else if (l.isUndefined()) { l_ins = w.immd(js_NaN); } else if (l.isString()) { + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, l_ins, cx_ins }; l_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else if (l.isNull()) { l_ins = w.immd(0.0); } else { @@ -9062,13 +9118,17 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond) } } if (!r.isNumber()) { - LIns* args[] = { r_ins, cx_ins }; if (r.isBoolean()) { r_ins = w.i2d(r_ins); } else if (r.isUndefined()) { r_ins = w.immd(js_NaN); } else if (r.isString()) { + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, r_ins, cx_ins }; r_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else if (r.isNull()) { r_ins = w.immd(0.0); } else { @@ -9176,16 +9236,30 @@ TraceRecorder::binary(LOpcode op) if (l.isString()) { NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() - LIns* args[] = { a, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, a, cx_ins }; a = w.call(&js_StringToNumber_ci, args); - lnum = js_StringToNumber(cx, l.toString()); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + lnum = js_StringToNumber(cx, l.toString(), &ok); + if (!ok) + RETURN_ERROR("oom"); leftIsNumber = true; } if (r.isString()) { NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() - LIns* args[] = { b, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, b, cx_ins }; b = w.call(&js_StringToNumber_ci, args); - rnum = js_StringToNumber(cx, r.toString()); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + JSBool ok; + rnum = js_StringToNumber(cx, r.toString(), &ok); + if (!ok) + RETURN_ERROR("oom"); rightIsNumber = true; } if (l.isBoolean()) { @@ -10706,8 +10780,13 @@ TraceRecorder::record_JSOP_NEG() } if (v.isString()) { - LIns* args[] = { get(&v), cx_ins }; - set(&v, w.negd(w.call(&js_StringToNumber_ci, args))); + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, get(&v), cx_ins }; + LIns* num_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + set(&v, w.negd(num_ins)); return ARECORD_CONTINUE; } @@ -10739,8 +10818,13 @@ TraceRecorder::record_JSOP_POS() } if (v.isString()) { - LIns* args[] = { get(&v), cx_ins }; - set(&v, w.call(&js_StringToNumber_ci, args)); + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, get(&v), cx_ins }; + LIns* num_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); + set(&v, num_ins); return ARECORD_CONTINUE; } @@ -12425,7 +12509,9 @@ TraceRecorder::getCharCodeAt(JSString *str, LIns* str_ins, LIns* idx_ins, LIns** LIns *lengthAndFlags_ins = w.ldpStringLengthAndFlags(str_ins); if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins, w.nameImmw(JSString::ROPE_BIT))))) { - w.call(&js_Flatten_ci, &str_ins); + LIns *args[] = { str_ins, cx_ins }; + LIns *ok_ins = w.call(&js_Flatten_ci, args); + guard(false, w.eqi0(ok_ins), OOM_EXIT); w.label(mbr); } @@ -12457,7 +12543,9 @@ TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins, w.nameImmw(JSString::ROPE_BIT))))) { - w.call(&js_Flatten_ci, &str_ins); + LIns *args[] = { str_ins, cx_ins }; + LIns *ok_ins = w.call(&js_Flatten_ci, args); + guard(false, w.eqi0(ok_ins), OOM_EXIT); w.label(mbr); } @@ -12827,8 +12915,12 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) } else if (v.isUndefined()) { typed_v_ins = w.immd(js_NaN); } else if (v.isString()) { - LIns* args[] = { typed_v_ins, cx_ins }; + LIns* ok_ins = w.allocp(sizeof(JSBool)); + LIns* args[] = { ok_ins, typed_v_ins, cx_ins }; typed_v_ins = w.call(&js_StringToNumber_ci, args); + guard(false, + w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"), + OOM_EXIT); } else if (v.isBoolean()) { JS_ASSERT(v.isBoolean()); typed_v_ins = w.i2d(typed_v_ins); @@ -14025,14 +14117,14 @@ TraceRecorder::record_JSOP_LOOKUPSWITCH() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_STRICTEQ() { - strictEquality(true, false); + CHECK_STATUS_A(strictEquality(true, false)); return ARECORD_CONTINUE; } JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_STRICTNE() { - strictEquality(false, false); + CHECK_STATUS_A(strictEquality(false, false)); return ARECORD_CONTINUE; } @@ -14937,7 +15029,7 @@ TraceRecorder::record_JSOP_CONDSWITCH() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_CASE() { - strictEquality(true, true); + CHECK_STATUS_A(strictEquality(true, true)); return ARECORD_CONTINUE; } @@ -15315,7 +15407,7 @@ TraceRecorder::record_JSOP_GOSUBX() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_CASEX() { - strictEquality(true, true); + CHECK_STATUS_A(strictEquality(true, true)); return ARECORD_CONTINUE; } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 3558a86c6169..9e1f47c5b4cd 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1300,7 +1300,7 @@ class TraceRecorder JS_REQUIRES_STACK RecordingStatus incElem(jsint incr, bool pre = true); JS_REQUIRES_STACK AbortableRecordingStatus incName(jsint incr, bool pre = true); - JS_REQUIRES_STACK void strictEquality(bool equal, bool cmpCase); + JS_REQUIRES_STACK RecordingStatus strictEquality(bool equal, bool cmpCase); JS_REQUIRES_STACK AbortableRecordingStatus equality(bool negate, bool tryBranchAfterCond); JS_REQUIRES_STACK AbortableRecordingStatus equalityHelper(Value& l, Value& r, nanojit::LIns* l_ins, nanojit::LIns* r_ins, diff --git a/js/src/jsutil.h b/js/src/jsutil.h index d735a4adeed0..6962ba8540ed 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -357,10 +357,11 @@ PodArrayZero(T (&t)[N]) template JS_ALWAYS_INLINE static void -PodCopy(T *dst, T *src, size_t nelem) +PodCopy(T *dst, const T *src, size_t nelem) { + JS_ASSERT(abs(dst - src) >= ptrdiff_t(nelem)); if (nelem < 128) { - for (T *srcend = src + nelem; src != srcend; ++src, ++dst) + for (const T *srcend = src + nelem; src != srcend; ++src, ++dst) *dst = *src; } else { memcpy(dst, src, nelem * sizeof(T)); diff --git a/js/src/jsval.h b/js/src/jsval.h index 036191c61b9c..0d9a2a42faa6 100644 --- a/js/src/jsval.h +++ b/js/src/jsval.h @@ -352,7 +352,7 @@ typedef union jsval_layout static JS_ALWAYS_INLINE JSBool JSVAL_IS_DOUBLE_IMPL(jsval_layout l) { - return (uint32)l.s.tag < (uint32)JSVAL_TAG_CLEAR; + return (uint32)l.s.tag <= (uint32)JSVAL_TAG_CLEAR; } static JS_ALWAYS_INLINE jsval_layout diff --git a/js/src/jsvector.h b/js/src/jsvector.h index bc93ee753e63..ae8fab42bf1d 100644 --- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -265,6 +265,10 @@ class Vector : AllocPolicy /* accessors */ + const AllocPolicy &allocPolicy() const { + return *this; + } + enum { InlineLength = N }; size_t length() const { diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp index 56c5e85fab5c..2878c2dc8100 100644 --- a/js/src/jsxdrapi.cpp +++ b/js/src/jsxdrapi.cpp @@ -456,13 +456,12 @@ JS_XDRString(JSXDRState *xdr, JSString **strp) if (!JS_XDRUint32(xdr, &nchars)) return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) { + if (xdr->mode == JSXDR_DECODE) chars = (jschar *) xdr->cx->malloc((nchars + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - } else { - chars = (*strp)->chars(); - } + else + chars = const_cast((*strp)->getChars(xdr->cx)); + if (!chars) + return JS_FALSE; if (!XDRChars(xdr, chars, nchars)) goto bad; diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index 24fdc6402245..bde8d9f07db1 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -158,36 +158,6 @@ const char js_leftcurly_entity_str[] = "{"; static JSBool GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); -static JS_INLINE JSString * -GetPrefix(const JSObject *obj) -{ - jsval v = obj->getNamePrefix(); - if (JSVAL_IS_VOID(v)) - return NULL; - JS_ASSERT(JSVAL_IS_STRING(v)); - return JSVAL_TO_STRING(v); -} - -static JSString * -GetURI(const JSObject *obj) -{ - jsval v = obj->getNameURI(); - if (JSVAL_IS_VOID(v)) - return NULL; - JS_ASSERT(JSVAL_IS_STRING(v)); - return JSVAL_TO_STRING(v); -} - -static JSString * -GetLocalName(const JSObject *obj) -{ - jsval v = obj->getQNameLocalName(); - if (JSVAL_IS_VOID(v)) - return NULL; - JS_ASSERT(JSVAL_IS_STRING(v)); - return JSVAL_TO_STRING(v); -} - static JSBool IsDeclared(const JSObject *obj) { @@ -209,9 +179,10 @@ xml_isXMLName(JSContext *cx, uintN argc, jsval *vp) static inline bool AppendString(JSCharBuffer &cb, JSString *str) { - const jschar *chars, *end; - str->getCharsAndEnd(chars, end); - return cb.append(chars, end); + const jschar *chars = str->getChars(cb.allocPolicy().context()); + if (!chars) + return false; + return cb.append(chars, str->length()); } /* @@ -240,9 +211,9 @@ NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp) * Namespace class and library functions. */ DEFINE_GETTER(NamePrefix_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefix()) + if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefixVal()) DEFINE_GETTER(NameURI_getter, - if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURI()) + if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURIVal()) static void namespace_finalize(JSContext *cx, JSObject *obj) @@ -260,7 +231,7 @@ namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp) obj2 = v->toObjectOrNull(); *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass) ? JS_FALSE - : js_EqualStrings(GetURI(obj), GetURI(obj2)); + : EqualStrings(obj->getNameURI(), obj2->getNameURI()); return JS_TRUE; } @@ -310,7 +281,7 @@ namespace_toString(JSContext *cx, uintN argc, Value *vp) obj = ComputeThisFromVp(cx, vp); if (!JS_InstanceOf(cx, obj, Jsvalify(&js_NamespaceClass), Jsvalify(vp + 2))) return JS_FALSE; - *vp = Valueify(obj->getNameURI()); + *vp = Valueify(obj->getNameURIVal()); return JS_TRUE; } @@ -320,20 +291,20 @@ static JSFunctionSpec namespace_methods[] = { }; static JSObject * -NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared) +NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBool declared) { JSObject *obj; obj = NewBuiltinClassInstanceXML(cx, &js_NamespaceClass); if (!obj) return JS_FALSE; - JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix())); - JS_ASSERT(JSVAL_IS_VOID(obj->getNameURI())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared())); if (prefix) - obj->setNamePrefix(STRING_TO_JSVAL(prefix)); + obj->setNamePrefix(prefix); if (uri) - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); if (declared) obj->setNamespaceDeclared(JSVAL_TRUE); METER(xml_stats.xmlnamespace); @@ -345,10 +316,10 @@ NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared) */ DEFINE_GETTER(QNameNameURI_getter, if (obj->getClass() == &js_QNameClass) - *vp = JSVAL_IS_VOID(obj->getNameURI()) ? JSVAL_NULL : obj->getNameURI()) + *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal()) DEFINE_GETTER(QNameLocalName_getter, if (obj->getClass() == &js_QNameClass) - *vp = obj->getQNameLocalName()) + *vp = obj->getQNameLocalNameVal()) static void anyname_finalize(JSContext* cx, JSObject* obj) @@ -361,14 +332,14 @@ anyname_finalize(JSContext* cx, JSObject* obj) static JSBool qname_identity(JSObject *qna, JSObject *qnb) { - JSString *uri1 = GetURI(qna); - JSString *uri2 = GetURI(qnb); + JSLinearString *uri1 = qna->getNameURI(); + JSLinearString *uri2 = qnb->getNameURI(); if (!uri1 ^ !uri2) return JS_FALSE; - if (uri1 && !js_EqualStrings(uri1, uri2)) + if (uri1 && !EqualStrings(uri1, uri2)) return JS_FALSE; - return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb)); + return EqualStrings(qna->getQNameLocalName(), qnb->getQNameLocalName()); } static JSBool @@ -474,7 +445,7 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) return JS_FALSE; } - uri = GetURI(obj); + uri = obj->getNameURI(); if (!uri) { /* No uri means wildcard qualifier. */ str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); @@ -487,7 +458,7 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) if (!str) return JS_FALSE; } - str = js_ConcatStrings(cx, str, GetLocalName(obj)); + str = js_ConcatStrings(cx, str, obj->getQNameLocalName()); if (!str) return JS_FALSE; @@ -497,7 +468,12 @@ qname_toString(JSContext *cx, uintN argc, Value *vp) if (!chars) return JS_FALSE; *chars = '@'; - js_strncpy(chars + 1, str->chars(), length); + const jschar *strChars = str->getChars(cx); + if (!strChars) { + cx->free(chars); + return JS_FALSE; + } + js_strncpy(chars + 1, strChars, length); chars[++length] = 0; str = js_NewString(cx, chars, length); if (!str) { @@ -517,23 +493,23 @@ static JSFunctionSpec qname_methods[] = { static void -InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix, - JSString *localName) +InitXMLQName(JSObject *obj, JSLinearString *uri, JSLinearString *prefix, + JSLinearString *localName) { - JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix())); - JS_ASSERT(JSVAL_IS_VOID(obj->getNameURI())); - JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalName())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); + JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal())); + JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal())); if (uri) - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); if (prefix) - obj->setNamePrefix(STRING_TO_JSVAL(prefix)); + obj->setNamePrefix(prefix); if (localName) - obj->setQNameLocalName(STRING_TO_JSVAL(localName)); + obj->setQNameLocalName(localName); } static JSObject * -NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName, - Class *clasp = &js_QNameClass) +NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, + JSLinearString *localName, Class *clasp = &js_QNameClass) { JSObject *obj = NewBuiltinClassInstanceXML(cx, clasp); if (!obj) @@ -585,7 +561,7 @@ IsXMLName(const jschar *cp, size_t n) JSBool js_IsXMLName(JSContext *cx, jsval v) { - JSString *name; + JSLinearString *name = NULL; JSErrorReporter older; /* @@ -596,10 +572,12 @@ js_IsXMLName(JSContext *cx, jsval v) */ if (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isQName()) { - name = GetLocalName(JSVAL_TO_OBJECT(v)); + name = JSVAL_TO_OBJECT(v)->getQNameLocalName(); } else { older = JS_SetErrorReporter(cx, NULL); - name = js_ValueToString(cx, Valueify(v)); + JSString *str = js_ValueToString(cx, Valueify(v)); + if (str) + name = str->ensureLinear(cx); JS_SetErrorReporter(cx, older); if (!name) { JS_ClearPendingException(cx); @@ -622,7 +600,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, JSObject *uriobj; JSBool isNamespace, isQName; Class *clasp; - JSString *empty, *uri, *prefix; + JSLinearString *empty, *prefix, *uri; isNamespace = isQName = JS_FALSE; #ifdef __GNUC__ /* suppress bogus gcc warnings */ @@ -656,42 +634,47 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, METER(xml_stats.xmlnamespace); empty = cx->runtime->emptyString; - obj->setNamePrefix(STRING_TO_JSVAL(empty)); - obj->setNameURI(STRING_TO_JSVAL(empty)); + obj->setNamePrefix(empty); + obj->setNameURI(empty); if (argc == 1 || argc == -1) { if (isNamespace) { obj->setNameURI(uriobj->getNameURI()); obj->setNamePrefix(uriobj->getNamePrefix()); - } else if (isQName && (uri = GetURI(uriobj))) { - obj->setNameURI(STRING_TO_JSVAL(uri)); + } else if (isQName && (uri = uriobj->getNameURI())) { + obj->setNameURI(uri); obj->setNamePrefix(uriobj->getNamePrefix()); } else { - uri = js_ValueToString(cx, Valueify(urival)); + JSString *str = js_ValueToString(cx, Valueify(urival)); + if (!str) + return JS_FALSE; + uri = str->ensureLinear(cx); if (!uri) return JS_FALSE; - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); if (!uri->empty()) - obj->setNamePrefix(JSVAL_VOID); + obj->clearNamePrefix(); } } else if (argc == 2) { - if (!isQName || !(uri = GetURI(uriobj))) { - uri = js_ValueToString(cx, Valueify(urival)); + if (!isQName || !(uri = uriobj->getNameURI())) { + JSString *str = js_ValueToString(cx, Valueify(urival)); + if (!str) + return JS_FALSE; + uri = str->ensureLinear(cx); if (!uri) return JS_FALSE; } - obj->setNameURI(STRING_TO_JSVAL(uri)); + obj->setNameURI(uri); prefixval = argv[0]; if (uri->empty()) { if (!JSVAL_IS_VOID(prefixval)) { - prefix = js_ValueToString(cx, Valueify(prefixval)); - if (!prefix) + JSString *str = js_ValueToString(cx, Valueify(prefixval)); + if (!str) return JS_FALSE; - if (!prefix->empty()) { - Value v = StringValue(prefix); + if (!str->empty()) { JSAutoByteString bytes; - if (js_ValueToPrintable(cx, v, &bytes)) { + if (js_ValueToPrintable(cx, StringValue(str), &bytes)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAMESPACE, bytes.ptr()); } @@ -699,12 +682,15 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, } } } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { - obj->setNamePrefix(JSVAL_VOID); + obj->clearNamePrefix(); } else { - prefix = js_ValueToString(cx, Valueify(prefixval)); + JSString *str = js_ValueToString(cx, Valueify(prefixval)); + if (!str) + return JS_FALSE; + prefix = str->ensureLinear(cx); if (!prefix) return JS_FALSE; - obj->setNamePrefix(STRING_TO_JSVAL(prefix)); + obj->setNamePrefix(prefix); } } return JS_TRUE; @@ -729,7 +715,7 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, jsval nameval, nsval; JSBool isQName, isNamespace; JSObject *qn; - JSString *uri, *prefix, *name; + JSLinearString *uri, *prefix, *name; JSObject *obj2; JS_ASSERT(clasp == &js_QNameClass || @@ -767,14 +753,14 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, /* If namespace is not specified and name is a QName, clone it. */ qn = JSVAL_TO_OBJECT(nameval); if (argc == 1) { - uri = GetURI(qn); - prefix = GetPrefix(qn); - name = GetLocalName(qn); + uri = qn->getNameURI(); + prefix = qn->getNamePrefix(); + name = qn->getQNameLocalName(); goto out; } /* Namespace and qname were passed -- use the qname's localName. */ - nameval = qn->getQNameLocalName(); + nameval = qn->getQNameLocalNameVal(); } if (argc == 0) { @@ -782,7 +768,10 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, } else if (argc < 0) { name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); } else { - name = js_ValueToString(cx, Valueify(nameval)); + JSString *str = js_ValueToString(cx, Valueify(nameval)); + if (!str) + return JS_FALSE; + name = str->ensureLinear(cx); if (!name) return JS_FALSE; argv[argc > 1] = STRING_TO_JSVAL(name); @@ -802,7 +791,7 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, if (JSVAL_IS_NULL(nsval)) { /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ - uri = prefix = NULL; + prefix = uri = NULL; } else { /* * Inline specialization of the Namespace constructor called with @@ -823,14 +812,17 @@ QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc, #endif if (isNamespace) { - uri = GetURI(obj2); - prefix = GetPrefix(obj2); - } else if (isQName && (uri = GetURI(obj2))) { + uri = obj2->getNameURI(); + prefix = obj2->getNamePrefix(); + } else if (isQName && (uri = obj2->getNameURI())) { JS_ASSERT(argc > 1); - prefix = GetPrefix(obj2); + prefix = obj2->getNamePrefix(); } else { JS_ASSERT(argc > 1); - uri = js_ValueToString(cx, Valueify(nsval)); + JSString *str = js_ValueToString(cx, Valueify(nsval)); + if (!str) + return JS_FALSE; + uri = str->ensureLinear(cx); if (!uri) return JS_FALSE; argv[0] = STRING_TO_JSVAL(uri); /* local root */ @@ -869,17 +861,17 @@ namespace_identity(const void *a, const void *b) { const JSObject *nsa = (const JSObject *) a; const JSObject *nsb = (const JSObject *) b; - JSString *prefixa = GetPrefix(nsa); - JSString *prefixb = GetPrefix(nsb); + JSLinearString *prefixa = nsa->getNamePrefix(); + JSLinearString *prefixb = nsb->getNamePrefix(); if (prefixa && prefixb) { - if (!js_EqualStrings(prefixa, prefixb)) + if (!EqualStrings(prefixa, prefixb)) return JS_FALSE; } else { if (prefixa || prefixb) return JS_FALSE; } - return js_EqualStrings(GetURI(nsa), GetURI(nsb)); + return EqualStrings(nsa->getNameURI(), nsb->getNameURI()); } static JSBool @@ -1180,16 +1172,17 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, JSXMLArray *inScopeNSes, JSBool isAttributeName) { JSContext *cx = parser->context; - JSString *str, *uri, *prefix, *localName; + JSLinearString *str, *uri, *prefix, *localName; size_t length, offset; const jschar *start, *limit, *colon; uint32 n; JSObject *ns; - JSString *nsprefix; + JSLinearString *nsprefix; JS_ASSERT(pn->pn_arity == PN_NULLARY); - str = ATOM_TO_STRING(pn->pn_atom); - str->getCharsAndLength(start, length); + str = pn->pn_atom; + start = str->chars(); + length = str->length(); JS_ASSERT(length != 0 && *start != '@'); JS_ASSERT(length != 1 || *start != '*'); @@ -1204,11 +1197,11 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, if (STARTS_WITH_XML(start, offset)) { if (offset == 3) { - uri = JS_InternString(cx, xml_namespace_str); + uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str)); if (!uri) return NULL; } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { - uri = JS_InternString(cx, xmlns_namespace_str); + uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xmlns_namespace_str)); if (!uri) return NULL; } else { @@ -1220,9 +1213,9 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject); - nsprefix = GetPrefix(ns); - if (nsprefix && js_EqualStrings(nsprefix, prefix)) { - uri = GetURI(ns); + nsprefix = ns->getNamePrefix(); + if (nsprefix && EqualStrings(nsprefix, prefix)) { + uri = ns->getNameURI(); break; } } @@ -1257,9 +1250,9 @@ ParseNodeToQName(Parser *parser, JSParseNode *pn, while (n != 0) { --n; ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject); - nsprefix = GetPrefix(ns); + nsprefix = ns->getNamePrefix(); if (!nsprefix || nsprefix->empty()) { - uri = GetURI(ns); + uri = ns->getNameURI(); break; } } @@ -1278,7 +1271,11 @@ ChompXMLWhitespace(JSContext *cx, JSString *str) const jschar *cp, *start, *end; jschar c; - str->getCharsAndLength(start, length); + length = str->length(); + start = str->getChars(cx); + if (!start) + return NULL; + for (cp = start, end = cp + length; cp < end; cp++) { c = *cp; if (!JS_ISXMLSPACE(c)) @@ -1303,7 +1300,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, { JSContext *cx = parser->context; JSXML *xml, *kid, *attr, *attrj; - JSString *str; + JSLinearString *str; uint32 length, n, i, j; JSParseNode *pn2, *pn3, *head, **pnp; JSObject *ns; @@ -1372,7 +1369,7 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, /* XXX where is this documented in an XML spec, or in E4X? */ if ((flags & XSF_IGNORE_WHITESPACE) && n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid->xml_value); + JSString *str = ChompXMLWhitespace(cx, kid->xml_value); if (!str) goto fail; kid->xml_value = str; @@ -1461,17 +1458,18 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, } } - str = ATOM_TO_STRING(pn2->pn_atom); + JSAtom *atom = pn2->pn_atom; pn2 = pn2->pn_next; JS_ASSERT(pn2); if (pn2->pn_type != TOK_XMLATTR) goto syntax; - str->getCharsAndLength(chars, length); + chars = atom->chars(); + length = atom->length(); if (length >= 5 && IS_XMLNS_CHARS(chars) && (length == 5 || chars[5] == ':')) { - JSString *uri, *prefix; + JSLinearString *uri, *prefix; uri = ATOM_TO_STRING(pn2->pn_atom); if (length == 5) { @@ -1545,8 +1543,8 @@ ParseNodeToXML(Parser *parser, JSParseNode *pn, for (j = 0; j < i; j++) { attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); attrjqn = attrj->name; - if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) && - js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) { + if (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) && + EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) { Value v = StringValue(ATOM_TO_STRING(pn2->pn_atom)); JSAutoByteString bytes; if (js_ValueToPrintable(cx, v, &bytes)) { @@ -1701,7 +1699,7 @@ static JSXML * ParseXMLSource(JSContext *cx, JSString *src) { jsval nsval; - JSString *uri; + JSLinearString *uri; size_t urilen, srclen, length, offset, dstlen; jschar *chars; const jschar *srcp, *endp; @@ -1718,7 +1716,7 @@ ParseXMLSource(JSContext *cx, JSString *src) if (!js_GetDefaultXMLNamespace(cx, &nsval)) return NULL; - uri = GetURI(JSVAL_TO_OBJECT(nsval)); + uri = JSVAL_TO_OBJECT(nsval)->getNameURI(); uri = js_EscapeAttributeValue(cx, uri, JS_FALSE); if (!uri) return NULL; @@ -1741,7 +1739,11 @@ ParseXMLSource(JSContext *cx, JSString *src) js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, &dstlen); offset += dstlen; - srcp = src->chars(); + srcp = src->getChars(cx); + if (!srcp) { + cx->free(chars); + return NULL; + } js_strncpy(chars + offset, srcp, srclen); offset += srclen; dstlen = length - offset + 1; @@ -1773,8 +1775,10 @@ ParseXMLSource(JSContext *cx, JSString *src) Parser parser(cx); if (parser.init(chars, length, filename, lineno)) { JSObject *scopeChain = GetScopeChain(cx); - if (!scopeChain) + if (!scopeChain) { + cx->free(chars); return NULL; + } JSParseNode *pn = parser.parseXMLText(scopeChain, false); uintN flags; if (pn && GetXMLSettingFlags(cx, &flags)) { @@ -2071,9 +2075,10 @@ AppendAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *valstr) static JSString * EscapeElementValue(JSContext *cx, JSCharBuffer &cb, JSString *str, uint32 toSourceFlag) { - size_t length; - const jschar *start; - str->getCharsAndLength(start, length); + size_t length = str->length(); + const jschar *start = str->getChars(cx); + if (!start) + return NULL; for (const jschar *cp = start, *end = start + length; cp != end; ++cp) { jschar c = *cp; @@ -2114,13 +2119,14 @@ EscapeElementValue(JSContext *cx, JSCharBuffer &cb, JSString *str, uint32 toSour * * These functions mutate cb, leaving it empty. */ -static JSString * +static JSLinearString * EscapeAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *str, JSBool quote) { - size_t length; - const jschar *start; - str->getCharsAndLength(start, length); + size_t length = str->length(); + const jschar *start = str->getChars(cx); + if (!start) + return NULL; if (quote && !cb.append('"')) return NULL; @@ -2168,13 +2174,13 @@ EscapeAttributeValue(JSContext *cx, JSCharBuffer &cb, JSString *str, static JSObject * GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) { - JSString *uri, *prefix, *nsprefix; + JSLinearString *uri, *prefix, *nsprefix; JSObject *match, *ns; uint32 i, n; jsval argv[2]; - uri = GetURI(qn); - prefix = GetPrefix(qn); + uri = qn->getNameURI(); + prefix = qn->getNamePrefix(); JS_ASSERT(uri); if (!uri) { JSAutoByteString bytes; @@ -2221,11 +2227,11 @@ GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) * This spec bug leads to ToXMLString results that duplicate the * declared namespace. */ - if (js_EqualStrings(GetURI(ns), uri)) { - nsprefix = GetPrefix(ns); + if (EqualStrings(ns->getNameURI(), uri)) { + nsprefix = ns->getNamePrefix(); if (nsprefix == prefix || ((nsprefix && prefix) - ? js_EqualStrings(nsprefix, prefix) + ? EqualStrings(nsprefix, prefix) : (nsprefix ? nsprefix : prefix)->empty())) { match = ns; break; @@ -2247,8 +2253,8 @@ GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes) return match; } -static JSString * -GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) +static JSLinearString * +GeneratePrefix(JSContext *cx, JSLinearString *uri, JSXMLArray *decls) { const jschar *cp, *start, *end; size_t length, newlength, offset; @@ -2256,7 +2262,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) jschar *bp, *dp; JSBool done; JSObject *ns; - JSString *nsprefix, *prefix; + JSLinearString *nsprefix, *prefix; JS_ASSERT(!uri->empty()); @@ -2272,7 +2278,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) * This is necessary for various log10 uses below to be valid. */ if (decls->length == 0) - return JS_NewStringCopyZ(cx, "a"); + return js_NewStringCopyZ(cx, "a"); /* * Try peeling off the last filename suffix or pathname component till @@ -2280,7 +2286,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". */ - uri->getCharsAndEnd(start, end); + start = uri->chars(); + end = start + uri->length(); cp = end; while (--cp > start) { if (*cp == '.' || *cp == '/' || *cp == ':') { @@ -2323,7 +2330,7 @@ GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) done = JS_TRUE; for (i = 0, n = decls->length; i < n; i++) { ns = XMLARRAY_MEMBER(decls, i, JSObject); - if (ns && (nsprefix = GetPrefix(ns)) && + if (ns && (nsprefix = ns->getNamePrefix()) && nsprefix->length() == newlength && !memcmp(nsprefix->chars(), bp, newlength * sizeof(jschar))) { @@ -2367,13 +2374,13 @@ namespace_match(const void *a, const void *b) { const JSObject *nsa = (const JSObject *) a; const JSObject *nsb = (const JSObject *) b; - JSString *prefixa, *prefixb = GetPrefix(nsb); + JSLinearString *prefixa, *prefixb = nsb->getNamePrefix(); if (prefixb) { - prefixa = GetPrefix(nsa); - return prefixa && js_EqualStrings(prefixa, prefixb); + prefixa = nsa->getNamePrefix(); + return prefixa && EqualStrings(prefixa, prefixb); } - return js_EqualStrings(GetURI(nsa), GetURI(nsb)); + return EqualStrings(nsa->getNameURI(), nsb->getNameURI()); } /* ECMA-357 10.2.1 and 10.2.2 */ @@ -2385,7 +2392,8 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, { JSBool pretty, indentKids; JSCharBuffer cb(cx); - JSString *str, *prefix, *nsuri; + JSString *str; + JSLinearString *prefix, *nsuri; uint32 i, n, nextIndentLevel; JSObject *ns, *ns2; AutoNamespaceArray empty(cx), decls(cx), ancdecls(cx); @@ -2423,7 +2431,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, case JSXML_CLASS_PROCESSING_INSTRUCTION: /* Step 7. */ - return MakeXMLPIString(cx, cb, GetLocalName(xml->name), + return MakeXMLPIString(cx, cb, xml->name->getQNameLocalName(), xml->xml_value); case JSXML_CLASS_LIST: @@ -2467,7 +2475,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, continue; if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { /* NOTE: may want to exclude unused namespaces here. */ - ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_TRUE); + ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), JS_TRUE); if (!ns2 || !XMLARRAY_APPEND(cx, &decls.array, ns2)) goto out; } @@ -2505,7 +2513,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); if (!prefix) { /* * Create a namespace prefix that isn't used by any member of decls. @@ -2521,8 +2529,8 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, * This helps descendants inherit the namespace instead of redundantly * redeclaring it with generated prefixes in each descendant. */ - nsuri = GetURI(ns); - if (!GetPrefix(xml->name)) { + nsuri = ns->getNameURI(); + if (!xml->name->getNamePrefix()) { prefix = cx->runtime->emptyString; } else { prefix = GeneratePrefix(cx, nsuri, &ancdecls.array); @@ -2573,7 +2581,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, if (!AppendString(cb, prefix) || !cb.append(':')) goto out; } - if (!AppendString(cb, GetLocalName(xml->name))) + if (!AppendString(cb, xml->name->getQNameLocalName())) goto out; /* @@ -2593,14 +2601,14 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* 17(b)(ii): NULL means *undefined* here. */ - prefix = GetPrefix(ns2); + prefix = ns2->getNamePrefix(); if (!prefix) { - prefix = GeneratePrefix(cx, GetURI(ns2), &ancdecls.array); + prefix = GeneratePrefix(cx, ns2->getNameURI(), &ancdecls.array); if (!prefix) goto out; /* Again, we avoid copying ns2 until we know it's prefix-less. */ - ns2 = NewXMLNamespace(cx, prefix, GetURI(ns2), JS_TRUE); + ns2 = NewXMLNamespace(cx, prefix, ns2->getNameURI(), JS_TRUE); if (!ns2) goto out; @@ -2624,7 +2632,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, } /* 17(b)(iv). */ - if (!AppendString(cb, GetLocalName(attr->name))) + if (!AppendString(cb, attr->name->getQNameLocalName())) goto out; /* 17(d-g). */ @@ -2643,12 +2651,12 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* 17(c)(ii): NULL means *undefined* here. */ - prefix = GetPrefix(ns3); + prefix = ns3->getNamePrefix(); if (!prefix) { - prefix = GeneratePrefix(cx, GetURI(ns3), &ancdecls.array); + prefix = GeneratePrefix(cx, ns3->getNameURI(), &ancdecls.array); if (!prefix) goto out; - ns3->setNamePrefix(STRING_TO_JSVAL(prefix)); + ns3->setNamePrefix(prefix); } /* 17(c)(iii). */ @@ -2658,7 +2666,7 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, } /* 17(d-g). */ - if (!AppendAttributeValue(cx, cb, GetURI(ns3))) + if (!AppendAttributeValue(cx, cb, ns3->getNameURI())) goto out; } } @@ -2714,14 +2722,14 @@ XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, goto out; /* Step 26. */ - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); if (prefix && !prefix->empty()) { if (!AppendString(cb, prefix) || !cb.append(':')) goto out; } /* Step 27. */ - if (!AppendString(cb, GetLocalName(xml->name)) || !cb.append('>')) + if (!AppendString(cb, xml->name->getQNameLocalName()) || !cb.append('>')) goto out; } @@ -2773,13 +2781,15 @@ ToXMLString(JSContext *cx, jsval v, uint32 toSourceFlag) static JSObject * ToAttributeName(JSContext *cx, jsval v) { - JSString *name, *uri, *prefix; + JSLinearString *name, *uri, *prefix; JSObject *obj; Class *clasp; JSObject *qn; if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); + name = JSVAL_TO_STRING(v)->ensureLinear(cx); + if (!name) + return NULL; uri = prefix = cx->runtime->emptyString; } else { if (JSVAL_IS_PRIMITIVE(v)) { @@ -2795,14 +2805,17 @@ ToAttributeName(JSContext *cx, jsval v) if (clasp == &js_QNameClass) { qn = obj; - uri = GetURI(qn); - prefix = GetPrefix(qn); - name = GetLocalName(qn); + uri = qn->getNameURI(); + prefix = qn->getNamePrefix(); + name = qn->getQNameLocalName(); } else { if (clasp == &js_AnyNameClass) { name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); } else { - name = js_ValueToString(cx, Valueify(v)); + JSString *str = js_ValueToString(cx, Valueify(v)); + if (!str) + return NULL; + name = str->ensureLinear(cx); if (!name) return NULL; } @@ -2826,14 +2839,14 @@ static JSBool IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp) { JSAtom *atom; - JSString *uri; + JSLinearString *uri; atom = cx->runtime->atomState.functionNamespaceURIAtom; - uri = GetURI(qn); + uri = qn->getNameURI(); if (uri && (uri == ATOM_TO_STRING(atom) || - js_EqualStrings(uri, ATOM_TO_STRING(atom)))) { - return JS_ValueToId(cx, STRING_TO_JSVAL(GetLocalName(qn)), funidp); + EqualStrings(uri, ATOM_TO_STRING(atom)))) { + return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp); } *funidp = JSID_VOID; return JS_TRUE; @@ -2898,7 +2911,7 @@ ToXMLName(JSContext *cx, jsval v, jsid *funidp) if (js_IdIsIndex(ATOM_TO_JSID(atomizedName), &index)) goto bad; - if (*name->chars() == '@') { + if (*atomizedName->chars() == '@') { name = js_NewDependentString(cx, name, 1, name->length() - 1); if (!name) return NULL; @@ -2928,7 +2941,7 @@ bad: static JSBool AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) { - JSString *prefix, *prefix2; + JSLinearString *prefix, *prefix2; JSObject *match, *ns2; uint32 i, n, m; @@ -2936,12 +2949,12 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) return JS_TRUE; /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); if (!prefix) { match = NULL; for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); - if (ns2 && js_EqualStrings(GetURI(ns2), GetURI(ns))) { + if (ns2 && EqualStrings(ns2->getNameURI(), ns->getNameURI())) { match = ns2; break; } @@ -2949,7 +2962,7 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) return JS_FALSE; } else { - if (prefix->empty() && GetURI(xml->name)->empty()) + if (prefix->empty() && xml->name->getNameURI()->empty()) return JS_TRUE; match = NULL; #ifdef __GNUC__ /* suppress bogus gcc warnings */ @@ -2957,18 +2970,18 @@ AddInScopeNamespace(JSContext *cx, JSXML *xml, JSObject *ns) #endif for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); - if (ns2 && (prefix2 = GetPrefix(ns2)) && - js_EqualStrings(prefix2, prefix)) { + if (ns2 && (prefix2 = ns2->getNamePrefix()) && + EqualStrings(prefix2, prefix)) { match = ns2; m = i; break; } } - if (match && !js_EqualStrings(GetURI(match), GetURI(ns))) { + if (match && !EqualStrings(match->getNameURI(), ns->getNameURI())) { ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, JSObject); JS_ASSERT(ns2 == match); - match->setNamePrefix(JSVAL_VOID); + match->clearNamePrefix(); if (!AddInScopeNamespace(cx, xml, match)) return JS_FALSE; } @@ -3116,7 +3129,7 @@ DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) return NULL; qn = xml->name; if (qn) { - qn = NewXMLQName(cx, GetURI(qn), GetPrefix(qn), GetLocalName(qn)); + qn = NewXMLQName(cx, qn->getNameURI(), qn->getNamePrefix(), qn->getQNameLocalName()); if (!qn) { ok = JS_FALSE; goto out; @@ -3145,7 +3158,7 @@ DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject); if (!ns) continue; - ns2 = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), + ns2 = NewXMLNamespace(cx, ns->getNamePrefix(), ns->getNameURI(), IsDeclared(ns)); if (!ns2) { copy->xml_namespaces.length = i; @@ -3188,27 +3201,27 @@ static JSBool MatchAttrName(JSObject *nameqn, JSXML *attr) { JSObject *attrqn = attr->name; - JSString *localName = GetLocalName(nameqn); - JSString *uri; + JSLinearString *localName = nameqn->getQNameLocalName(); + JSLinearString *uri; return (IS_STAR(localName) || - js_EqualStrings(GetLocalName(attrqn), localName)) && - (!(uri = GetURI(nameqn)) || - js_EqualStrings(GetURI(attrqn), uri)); + EqualStrings(attrqn->getQNameLocalName(), localName)) && + (!(uri = nameqn->getNameURI()) || + EqualStrings(attrqn->getNameURI(), uri)); } static JSBool MatchElemName(JSObject *nameqn, JSXML *elem) { - JSString *localName = GetLocalName(nameqn); - JSString *uri; + JSLinearString *localName = nameqn->getQNameLocalName(); + JSLinearString *uri; return (IS_STAR(localName) || (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(GetLocalName(elem->name), localName))) && - (!(uri = GetURI(nameqn)) || + EqualStrings(elem->name->getQNameLocalName(), localName))) && + (!(uri = nameqn->getNameURI()) || (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(GetURI(elem->name), uri))); + EqualStrings(elem->name->getNameURI(), uri))); } /* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ @@ -3325,8 +3338,8 @@ retry: vqn = vxml->name; if (qn) { *bp = vqn && - js_EqualStrings(GetLocalName(qn), GetLocalName(vqn)) && - js_EqualStrings(GetURI(qn), GetURI(vqn)); + EqualStrings(qn->getQNameLocalName(), vqn->getQNameLocalName()) && + EqualStrings(qn->getNameURI(), vqn->getNameURI()); } else { *bp = vqn == NULL; } @@ -3334,7 +3347,8 @@ retry: return JS_TRUE; if (JSXML_HAS_VALUE(xml)) { - *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); + if (!EqualStrings(cx, xml->xml_value, vxml->xml_value, bp)) + return JS_FALSE; } else if (xml->xml_kids.length != vxml->xml_kids.length) { *bp = JS_FALSE; } else { @@ -3374,7 +3388,8 @@ retry: vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); if (!vattr) continue; - *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); + if (!EqualStrings(cx, attr->xml_value, vattr->xml_value, bp)) + return JS_FALSE; } } } @@ -3853,7 +3868,6 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) uint32 index, i, j, k, n, q, matchIndex; jsval attrval, nsval; jsid funid; - JSString *left, *right, *space, *uri; JSObject *ns; xml = (JSXML *) GetInstancePrivate(cx, obj, &js_XMLClass, NULL); @@ -3943,7 +3957,7 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) /* 2(c)(ii) is distributed below as several js_NewXML calls. */ targetprop = xml->xml_targetprop; - if (!targetprop || IS_STAR(GetLocalName(targetprop))) { + if (!targetprop || IS_STAR(targetprop->getQNameLocalName())) { /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ kid = js_NewXML(cx, JSXML_CLASS_TEXT); if (!kid) @@ -4047,8 +4061,8 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { nameobj = kid->name; if (nameobj->getClass() != &js_AttributeNameClass) { - nameobj = NewXMLQName(cx, GetURI(nameobj), GetPrefix(nameobj), - GetLocalName(nameobj), + nameobj = NewXMLQName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(), + nameobj->getQNameLocalName(), &js_AttributeNameClass); if (!nameobj) goto bad; @@ -4265,16 +4279,16 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (n == 0) { *vp = STRING_TO_JSVAL(cx->runtime->emptyString); } else { - left = KidToString(cx, vxml, 0); + JSString *left = KidToString(cx, vxml, 0); if (!left) goto bad; - space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); + JSString *space = cx->runtime->atomState.spaceAtom; for (i = 1; i < n; i++) { left = js_ConcatStrings(cx, left, space); if (!left) goto bad; - right = KidToString(cx, vxml, i); + JSString *right = KidToString(cx, vxml, i); if (!right) goto bad; left = js_ConcatStrings(cx, left, right); @@ -4298,10 +4312,9 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) if (!attr) continue; attrqn = attr->name; - if (js_EqualStrings(GetLocalName(attrqn), - GetLocalName(nameqn))) { - uri = GetURI(nameqn); - if (!uri || js_EqualStrings(GetURI(attrqn), uri)) { + if (EqualStrings(attrqn->getQNameLocalName(), nameqn->getQNameLocalName())) { + JSLinearString *uri = nameqn->getNameURI(); + if (!uri || EqualStrings(attrqn->getNameURI(), uri)) { if (!match) { match = attr; } else { @@ -4316,14 +4329,15 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) attr = match; if (!attr) { /* 7(f)(i-ii). */ - uri = GetURI(nameqn); + JSLinearString *uri = nameqn->getNameURI(); + JSLinearString *left, *right; if (!uri) { left = right = cx->runtime->emptyString; } else { left = uri; - right = GetPrefix(nameqn); + right = nameqn->getNamePrefix(); } - nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn)); + nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName()); if (!nameqn) goto bad; @@ -4355,13 +4369,13 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) /* 8-9. */ if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && - !IS_STAR(GetLocalName(nameqn))) { + !IS_STAR(nameqn->getQNameLocalName())) { goto out; } /* 10-11. */ id = JSID_VOID; - primitiveAssign = !vxml && !IS_STAR(GetLocalName(nameqn)); + primitiveAssign = !vxml && !IS_STAR(nameqn->getQNameLocalName()); /* 12. */ k = n = xml->xml_kids.length; @@ -4407,16 +4421,17 @@ PutProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) /* 13(b). */ if (primitiveAssign) { - uri = GetURI(nameqn); + JSLinearString *uri = nameqn->getNameURI(); + JSLinearString *left, *right; if (!uri) { ns = JSVAL_TO_OBJECT(nsval); - left = GetURI(ns); - right = GetPrefix(ns); + left = ns->getNameURI(); + right = ns->getNamePrefix(); } else { left = uri; - right = GetPrefix(nameqn); + right = nameqn->getNamePrefix(); } - nameqn = NewXMLQName(cx, left, right, GetLocalName(nameqn)); + nameqn = NewXMLQName(cx, left, right, nameqn->getQNameLocalName()); if (!nameqn) goto bad; @@ -4501,7 +4516,7 @@ ResolveValue(JSContext *cx, JSXML *list, JSXML **result) target = list->xml_target; targetprop = list->xml_targetprop; - if (!target || !targetprop || IS_STAR(GetLocalName(targetprop))) { + if (!target || !targetprop || IS_STAR(targetprop->getQNameLocalName())) { *result = NULL; return JS_TRUE; } @@ -4631,12 +4646,16 @@ static JSBool HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *found) { JSXML *xml; + bool isIndex; uint32 i; JSObject *qn; jsid funid; xml = (JSXML *) obj->getPrivate(); - if (js_IdValIsIndex(id, &i)) { + if (!js_IdValIsIndex(cx, id, &i, &isIndex)) + return JS_FALSE; + + if (isIndex) { *found = HasIndexedProperty(xml, i); } else { qn = ToXMLName(cx, id, &funid); @@ -5039,7 +5058,7 @@ js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) ok = (str = js_ValueToString(cx, ObjectValue(*obj))) && (vstr = js_ValueToString(cx, Valueify(v))); if (ok) - *bp = js_EqualStrings(str, vstr); + ok = EqualStrings(cx, str, vstr, bp); js_LeaveLocalRootScope(cx); } } else { @@ -5053,13 +5072,13 @@ js_TestXMLEquality(JSContext *cx, const Value &v1, const Value &v2, JSBool *bp) ok = (str = js_ValueToString(cx, ObjectValue(*obj))) && (vstr = js_ValueToString(cx, Valueify(v))); if (ok) - *bp = js_EqualStrings(str, vstr); + ok = EqualStrings(cx, str, vstr, bp); } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { str = js_ValueToString(cx, ObjectValue(*obj)); if (!str) { ok = JS_FALSE; } else if (JSVAL_IS_STRING(v)) { - *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); + ok = EqualStrings(cx, str, JSVAL_TO_STRING(v), bp); } else { ok = JS_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); if (ok) { @@ -5338,6 +5357,7 @@ static JSBool xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, jsval *rval) { + bool isIndex; uint32 index; JSXML *kid; JSObject *kidobj; @@ -5345,7 +5365,10 @@ xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, /* ECMA-357 13.4.4.6 */ JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - if (js_IdValIsIndex(name, &index)) { + if (!js_IdValIsIndex(cx, name, &index, &isIndex)) + return JS_FALSE; + + if (isIndex) { if (index >= JSXML_LENGTH(xml)) { *rval = JSVAL_VOID; } else { @@ -5731,7 +5754,7 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) { uint32 length, i, j, n; JSObject *ns, *ns2; - JSString *prefix, *prefix2; + JSLinearString *prefix, *prefix2; length = nsarray->length; do { @@ -5742,14 +5765,14 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) if (!ns) continue; - prefix = GetPrefix(ns); + prefix = ns->getNamePrefix(); for (j = 0; j < length; j++) { ns2 = XMLARRAY_MEMBER(nsarray, j, JSObject); if (ns2) { - prefix2 = GetPrefix(ns2); + prefix2 = ns2->getNamePrefix(); if ((prefix2 && prefix) - ? js_EqualStrings(prefix2, prefix) - : js_EqualStrings(GetURI(ns2), GetURI(ns))) { + ? EqualStrings(prefix2, prefix) + : EqualStrings(ns2->getNameURI(), ns->getNameURI())) { break; } } @@ -5885,7 +5908,7 @@ static JSBool xml_localName(JSContext *cx, uintN argc, jsval *vp) { NON_LIST_XML_METHOD_PROLOG; - *vp = xml->name ? xml->name->getQNameLocalName() : JSVAL_NULL; + *vp = xml->name ? xml->name->getQNameLocalNameVal() : JSVAL_NULL; return JS_TRUE; } @@ -5900,7 +5923,7 @@ xml_name(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_namespace(JSContext *cx, uintN argc, jsval *vp) { - JSString *prefix, *nsprefix; + JSLinearString *prefix, *nsprefix; jsuint i, length; JSObject *ns; @@ -5913,7 +5936,10 @@ xml_namespace(JSContext *cx, uintN argc, jsval *vp) if (argc == 0) { prefix = NULL; } else { - prefix = js_ValueToString(cx, Valueify(vp[2])); + JSString *str = js_ValueToString(cx, Valueify(vp[2])); + if (!str) + return false; + prefix = str->ensureLinear(cx); if (!prefix) return false; vp[2] = STRING_TO_JSVAL(prefix); /* local root */ @@ -5932,8 +5958,8 @@ xml_namespace(JSContext *cx, uintN argc, jsval *vp) for (i = 0, length = inScopeNSes.array.length; i < length; i++) { ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSObject); if (ns) { - nsprefix = GetPrefix(ns); - if (nsprefix && js_EqualStrings(nsprefix, prefix)) + nsprefix = ns->getNamePrefix(); + if (nsprefix && EqualStrings(nsprefix, prefix)) break; ns = NULL; } @@ -6119,7 +6145,6 @@ xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, JSObject *kidobj; jsval v; uint32 i, n; - JSString *localName; list = xml_list_helper(cx, xml, vp); if (!list) @@ -6160,9 +6185,9 @@ xml_processingInstructions_helper(JSContext *cx, JSObject *obj, JSXML *xml, for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { JSXML *kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { - localName = GetLocalName(nameqn); + JSLinearString *localName = nameqn->getQNameLocalName(); if (IS_STAR(localName) || - js_EqualStrings(localName, GetLocalName(kid->name))) { + EqualStrings(localName, kid->name->getQNameLocalName())) { ok = Append(cx, list, kid); if (!ok) break; @@ -6210,17 +6235,23 @@ xml_prependChild(JSContext *cx, uintN argc, jsval *vp) static JSBool xml_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp) { + bool isIndex; uint32 index; XML_METHOD_PROLOG; *vp = JSVAL_FALSE; - if (argc != 0 && js_IdValIsIndex(vp[2], &index)) { - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.18. */ - *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); - } else { - /* 13.4.4.30. */ - *vp = BOOLEAN_TO_JSVAL(index == 0); + if (argc != 0) { + if (!js_IdValIsIndex(cx, vp[2], &index, &isIndex)) + return JS_FALSE; + + if (isIndex) { + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.18. */ + *vp = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); + } else { + /* 13.4.4.30. */ + *vp = BOOLEAN_TO_JSVAL(index == 0); + } } } return JS_TRUE; @@ -6231,15 +6262,15 @@ namespace_full_match(const void *a, const void *b) { const JSObject *nsa = (const JSObject *) a; const JSObject *nsb = (const JSObject *) b; - JSString *prefixa = GetPrefix(nsa); - JSString *prefixb; + JSLinearString *prefixa = nsa->getNamePrefix(); + JSLinearString *prefixb; if (prefixa) { - prefixb = GetPrefix(nsb); - if (prefixb && !js_EqualStrings(prefixa, prefixb)) + prefixb = nsb->getNamePrefix(); + if (prefixb && !EqualStrings(prefixa, prefixb)) return JS_FALSE; } - return js_EqualStrings(GetURI(nsa), GetURI(nsb)); + return EqualStrings(nsa->getNameURI(), nsb->getNameURI()); } static JSBool @@ -6338,7 +6369,15 @@ xml_replace(JSContext *cx, uintN argc, jsval *vp) if (!xml) return JS_FALSE; - if (argc == 0 || !js_IdValIsIndex(vp[2], &index)) { + bool haveIndex; + if (argc == 0) { + haveIndex = true; + } else { + if (!js_IdValIsIndex(cx, vp[2], &index, &haveIndex)) + return JS_FALSE; + } + + if (!haveIndex) { /* * Call function QName per spec, not ToXMLName, to avoid attribute * names. @@ -6395,7 +6434,7 @@ xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) { jsval name; JSObject *nameqn; - JSString *namestr; + JSLinearString *namestr; NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) @@ -6408,19 +6447,22 @@ xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) { nameqn = JSVAL_TO_OBJECT(name); - namestr = GetLocalName(nameqn); + namestr = nameqn->getQNameLocalName(); } else { if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2])) return JS_FALSE; name = vp[2]; - namestr = JSVAL_TO_STRING(name); + namestr = JSVAL_TO_STRING(name)->ensureLinear(cx); + if (!namestr) + return JS_FALSE; } } xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; - xml->name->setQNameLocalName(namestr ? STRING_TO_JSVAL(namestr) : JSVAL_VOID); + if (namestr) + xml->name->setQNameLocalName(namestr); return JS_TRUE; } @@ -6444,8 +6486,8 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) name = vp[2]; if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass && - !GetURI(nameqn = JSVAL_TO_OBJECT(name))) { - name = vp[2] = nameqn->getQNameLocalName(); + !(nameqn = JSVAL_TO_OBJECT(name))->getNameURI()) { + name = vp[2] = nameqn->getQNameLocalNameVal(); } } @@ -6455,7 +6497,7 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) /* ECMA-357 13.4.4.35 Step 4. */ if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - nameqn->setNameURI(STRING_TO_JSVAL(cx->runtime->emptyString)); + nameqn->setNameURI(cx->runtime->emptyString); xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) @@ -6477,7 +6519,7 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) nsowner = xml->parent; } - if (GetPrefix(nameqn)) { + if (nameqn->getNamePrefix()) { /* * The name being set has a prefix, which originally came from some * namespace object (which may be the null namespace, where both the @@ -6510,18 +6552,18 @@ xml_setName(JSContext *cx, uintN argc, jsval *vp) * the constructor, because we know uri of nameqn is non-empty (so * prefix does not need to be converted from null to empty by QName). */ - JS_ASSERT(!GetURI(nameqn)->empty()); + JS_ASSERT(!nameqn->getNameURI()->empty()); nsarray = &nsowner->xml_namespaces; for (i = 0, n = nsarray->length; i < n; i++) { ns = XMLARRAY_MEMBER(nsarray, i, JSObject); - if (ns && js_EqualStrings(GetURI(ns), GetURI(nameqn))) { + if (ns && EqualStrings(ns->getNameURI(), nameqn->getNameURI())) { nameqn->setNamePrefix(ns->getNamePrefix()); return JS_TRUE; } } - ns = NewXMLNamespace(cx, NULL, GetURI(nameqn), JS_TRUE); + ns = NewXMLNamespace(cx, NULL, nameqn->getNameURI(), JS_TRUE); if (!ns) return JS_FALSE; } @@ -7175,13 +7217,13 @@ JSBool js_GetFunctionNamespace(JSContext *cx, Value *vp) { JSObject *obj; - JSString *prefix, *uri; + JSLinearString *prefix, *uri; obj = cx->compartment->functionNamespaceObject; if (!obj) { JSRuntime *rt = cx->runtime; - prefix = ATOM_TO_STRING(rt->atomState.typeAtoms[JSTYPE_FUNCTION]); - uri = ATOM_TO_STRING(rt->atomState.functionNamespaceURIAtom); + prefix = rt->atomState.typeAtoms[JSTYPE_FUNCTION]; + uri = rt->atomState.functionNamespaceURIAtom; obj = NewXMLNamespace(cx, prefix, uri, JS_FALSE); if (!obj) return false; @@ -7282,7 +7324,7 @@ js_ToAttributeName(JSContext *cx, Value *vp) return JS_TRUE; } -JSString * +JSLinearString * js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote) { JSCharBuffer cb(cx); @@ -7292,14 +7334,18 @@ js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote) JSString * js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) { - size_t len, len2, newlen; - const jschar *chars, *chars2; - jschar *newchars; + size_t len = str->length(); + const jschar *chars = str->getChars(cx); + if (!chars) + return NULL; - str->getCharsAndLength(chars, len); - str2->getCharsAndLength(chars2, len2); - newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; - newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar)); + size_t len2 = str2->length(); + const jschar *chars2 = str2->getChars(cx); + if (!chars2) + return NULL; + + size_t newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; + jschar *newchars = (jschar *) cx->malloc((newlen+1) * sizeof(jschar)); if (!newchars) return NULL; @@ -7722,7 +7768,10 @@ js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, return NULL; xml = (JSXML *) obj->getPrivate(); if (name) { - qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, name); + JSLinearString *linearName = name->ensureLinear(cx); + if (!linearName) + return NULL; + qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName); if (!qn) return NULL; xml->name = qn; diff --git a/js/src/jsxml.h b/js/src/jsxml.h index 58c2bb875522..fe3fa184f2f7 100644 --- a/js/src/jsxml.h +++ b/js/src/jsxml.h @@ -312,7 +312,7 @@ js_IsXMLName(JSContext *cx, jsval v); extern JSBool js_ToAttributeName(JSContext *cx, js::Value *vp); -extern JSString * +extern JSLinearString * js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote); extern JSString * diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 4d4f0fa36a1d..e04989b0b64b 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2830,7 +2830,8 @@ mjit::Compiler::compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const JS_ASSERT(rhs.isPrimitive()); if (lhs.isString() && rhs.isString()) { - int cmp = js_CompareStrings(lhs.toString(), rhs.toString()); + int32 cmp; + CompareStrings(cx, lhs.toString(), rhs.toString(), &cmp); switch (op) { case JSOP_LT: return cmp < 0; diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 3e604b362f24..11eccaedcdf0 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1562,7 +1562,8 @@ mjit::Compiler::jsop_stricteq(JSOp op) /* Constant-fold. */ if (lhs->isConstant() && rhs->isConstant()) { - bool b = StrictlyEqual(cx, lhs->getValue(), rhs->getValue()); + JSBool b; + StrictlyEqual(cx, lhs->getValue(), rhs->getValue(), &b); frame.popn(2); frame.push(BooleanValue((op == JSOP_STRICTEQ) ? b : !b)); return; diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 10bff0a6af3c..f109b65e51c0 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -2136,7 +2136,7 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i CodeLocationLabel cs = buffer.finalize(); #if DEBUG - char *chars = js_DeflateString(cx, v.toString()->chars(), v.toString()->length()); + char *chars = js_DeflateString(cx, v.toString()->nonRopeChars(), v.toString()->length()); JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n", js_CodeName[op], cs.executableAddress(), id, chars, holder->shape(), cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp())); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index d07345a29a2e..e038a429d8f3 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -940,7 +940,10 @@ template void JS_FASTCALL stubs::DefFun(VMFrame &f, JSFunction *fun); DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ if (lval.isString() && rval.isString()) { \ JSString *l = lval.toString(), *r = rval.toString(); \ - cond = js_CompareStrings(l, r) OP 0; \ + JSBool cmp; \ + if (!CompareStrings(cx, l, r, &cmp)) \ + THROWV(JS_FALSE); \ + cond = cmp OP 0; \ } else { \ double l, r; \ if (!ValueToNumber(cx, lval, &l) || \ @@ -1006,7 +1009,10 @@ StubEqualityOp(VMFrame &f) if (lval.isString() && rval.isString()) { JSString *l = lval.toString(); JSString *r = rval.toString(); - cond = js_EqualStrings(l, r) == EQ; + JSBool equal; + if (!EqualStrings(cx, l, r, &equal)) + return false; + cond = equal == EQ; } else #if JS_HAS_XML_SUPPORT if ((lval.isObject() && lval.toObject().isXML()) || @@ -1066,7 +1072,10 @@ StubEqualityOp(VMFrame &f) if (lval.isString() && rval.isString()) { JSString *l = lval.toString(); JSString *r = rval.toString(); - cond = js_EqualStrings(l, r) == EQ; + JSBool equal; + if (!EqualStrings(cx, l, r, &equal)) + return false; + cond = equal == EQ; } else { double l, r; if (!ValueToNumber(cx, lval, &l) || @@ -2302,9 +2311,11 @@ stubs::StrictEq(VMFrame &f) { const Value &rhs = f.regs.sp[-1]; const Value &lhs = f.regs.sp[-2]; - const bool b = StrictlyEqual(f.cx, lhs, rhs) == true; + JSBool equal; + if (!StrictlyEqual(f.cx, lhs, rhs, &equal)) + THROW(); f.regs.sp--; - f.regs.sp[-1].setBoolean(b); + f.regs.sp[-1].setBoolean(equal == true); } void JS_FASTCALL @@ -2312,9 +2323,11 @@ stubs::StrictNe(VMFrame &f) { const Value &rhs = f.regs.sp[-1]; const Value &lhs = f.regs.sp[-2]; - const bool b = StrictlyEqual(f.cx, lhs, rhs) != true; + JSBool equal; + if (!StrictlyEqual(f.cx, lhs, rhs, &equal)) + THROW(); f.regs.sp--; - f.regs.sp[-1].setBoolean(b); + f.regs.sp[-1].setBoolean(equal != true); } void JS_FASTCALL @@ -2487,13 +2500,15 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc) JS_ASSERT(npairs); if (lval.isString()) { - JSString *str = lval.toString(); + JSLinearString *str = lval.toString()->ensureLinear(f.cx); + if (!str) + THROWV(NULL); for (uint32 i = 1; i <= npairs; i++) { Value rval = script->getConst(GET_INDEX(pc)); pc += INDEX_LEN; if (rval.isString()) { - JSString *rhs = rval.toString(); - if (rhs == str || js_EqualStrings(str, rhs)) { + JSLinearString *rhs = rval.toString()->assertIsLinear(); + if (rhs == str || EqualStrings(str, rhs)) { void* native = script->nativeCodeForPC(ctor, jpc + GET_JUMP_OFFSET(pc)); JS_ASSERT(native); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 9892438615ce..ce4ddeff0ef6 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3402,7 +3402,12 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp) if (saveCurrent) oldfp = JS_SaveFrameChain(cx); - JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, str->chars(), str->length(), + size_t length; + const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) + return JS_FALSE; + + JSBool ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, fp->script()->filename, JS_PCToLineNumber(cx, fp->script(), fi.pc()), diff --git a/js/src/yarr/jswtfbridge.h b/js/src/yarr/jswtfbridge.h index 2ed79102bf4e..ef0f72b1739c 100644 --- a/js/src/yarr/jswtfbridge.h +++ b/js/src/yarr/jswtfbridge.h @@ -50,7 +50,7 @@ #include "jstl.h" typedef jschar UChar; -typedef JSString UString; +typedef JSLinearString UString; template class ValueDeleter