Bug 609440, part 4 - make JSString::chars() fallible (r=waldo,dvander,igor,dwitte,njn)

This commit is contained in:
Luke Wagner 2010-12-06 10:26:58 -08:00
parent 56b2810a26
commit e4bb2ca2a9
54 changed files with 1749 additions and 1216 deletions

View File

@ -1292,7 +1292,10 @@ StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
{ {
JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact); JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
const jschar* cp = string->chars(); const jschar* cp = string->getChars(NULL);
if (!cp)
return false;
const jschar* end = cp + string->length(); const jschar* end = cp + string->length();
if (cp == end) if (cp == end)
return false; return false;
@ -1780,9 +1783,10 @@ ImplicitConvert(JSContext* cx,
JSString* str = JSVAL_TO_STRING(val); \ JSString* str = JSVAL_TO_STRING(val); \
if (str->length() != 1) \ if (str->length() != 1) \
return TypeError(cx, #name, val); \ return TypeError(cx, #name, val); \
\ const jschar *chars = str->getChars(cx); \
result = str->chars()[0]; \ if (!chars) \
\ return false; \
result = chars[0]; \
} else if (!jsvalToInteger(cx, val, &result)) { \ } else if (!jsvalToInteger(cx, val, &result)) { \
return TypeError(cx, #name, val); \ return TypeError(cx, #name, val); \
} \ } \
@ -1824,8 +1828,10 @@ ImplicitConvert(JSContext* cx,
// which the caller assumes ownership of. // which the caller assumes ownership of.
// TODO: Extend this so we can safely convert strings at other times also. // TODO: Extend this so we can safely convert strings at other times also.
JSString* sourceString = JSVAL_TO_STRING(val); JSString* sourceString = JSVAL_TO_STRING(val);
const jschar* sourceChars = sourceString->chars();
size_t sourceLength = sourceString->length(); size_t sourceLength = sourceString->length();
const jschar* sourceChars = sourceString->getChars(cx);
if (!sourceChars)
return false;
switch (CType::GetTypeCode(cx, baseType)) { switch (CType::GetTypeCode(cx, baseType)) {
case TYPE_char: case TYPE_char:
@ -1879,8 +1885,10 @@ ImplicitConvert(JSContext* cx,
if (JSVAL_IS_STRING(val)) { if (JSVAL_IS_STRING(val)) {
JSString* sourceString = JSVAL_TO_STRING(val); JSString* sourceString = JSVAL_TO_STRING(val);
const jschar* sourceChars = sourceString->chars();
size_t sourceLength = sourceString->length(); size_t sourceLength = sourceString->length();
const jschar* sourceChars = sourceString->getChars(cx);
if (!sourceChars)
return false;
switch (CType::GetTypeCode(cx, baseType)) { switch (CType::GetTypeCode(cx, baseType)) {
case TYPE_char: case TYPE_char:
@ -1989,21 +1997,18 @@ ImplicitConvert(JSContext* cx,
if (JSID_IS_VOID(id)) if (JSID_IS_VOID(id))
break; break;
js::AutoValueRooter fieldVal(cx); if (!JSID_IS_STRING(id)) {
JS_IdToValue(cx, id, fieldVal.jsval_addr());
if (!JSVAL_IS_STRING(fieldVal.jsval_value())) {
JS_ReportError(cx, "property name is not a string"); JS_ReportError(cx, "property name is not a string");
return false; return false;
} }
const FieldInfo* field = StructType::LookupField(cx, targetType, JSFlatString *name = JSID_TO_FLAT_STRING(id);
JSVAL_TO_STRING(fieldVal.jsval_value())); const FieldInfo* field = StructType::LookupField(cx, targetType, name);
if (!field) if (!field)
return false; return false;
JSString* name = JSVAL_TO_STRING(fieldVal.jsval_value());
js::AutoValueRooter prop(cx); 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; return false;
// Convert the field via ImplicitConvert(). // 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, // We were given a string. Size the array to the appropriate length,
// including space for the terminator. // including space for the terminator.
JSString* sourceString = JSVAL_TO_STRING(argv[0]); JSString* sourceString = JSVAL_TO_STRING(argv[0]);
const jschar* sourceChars = sourceString->chars();
size_t sourceLength = sourceString->length(); size_t sourceLength = sourceString->length();
const jschar* sourceChars = sourceString->getChars(cx);
if (!sourceChars)
return false;
switch (CType::GetTypeCode(cx, baseType)) { switch (CType::GetTypeCode(cx, baseType)) {
case TYPE_char: 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 // For a struct field descriptor 'val' of the form { name : type }, extract
// 'name' and 'type'. // 'name' and 'type'.
static JSString* static JSFlatString*
ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj) ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
{ {
if (JSVAL_IS_PRIMITIVE(val)) { if (JSVAL_IS_PRIMITIVE(val)) {
@ -3885,23 +3892,21 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
return NULL; return NULL;
js::AutoObjectRooter iterroot(cx, iter); js::AutoObjectRooter iterroot(cx, iter);
jsid id; jsid nameid;
if (!JS_NextProperty(cx, iter, &id)) if (!JS_NextProperty(cx, iter, &nameid))
return NULL; return NULL;
if (JSID_IS_VOID(id)) { if (JSID_IS_VOID(nameid)) {
JS_ReportError(cx, "struct field descriptors require a valid name and type"); JS_ReportError(cx, "struct field descriptors require a valid name and type");
return NULL; return NULL;
} }
js::AutoValueRooter nameVal(cx); if (!JSID_IS_STRING(nameid)) {
JS_IdToValue(cx, id, nameVal.jsval_addr());
if (!JSVAL_IS_STRING(nameVal.jsval_value())) {
JS_ReportError(cx, "struct field descriptors require a valid name and type"); JS_ReportError(cx, "struct field descriptors require a valid name and type");
return NULL; return NULL;
} }
JSString* name = JSVAL_TO_STRING(nameVal.jsval_value());
// make sure we have one, and only one, property // make sure we have one, and only one, property
jsid id;
if (!JS_NextProperty(cx, iter, &id)) if (!JS_NextProperty(cx, iter, &id))
return NULL; return NULL;
if (!JSID_IS_VOID(id)) { if (!JSID_IS_VOID(id)) {
@ -3910,7 +3915,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
} }
js::AutoValueRooter propVal(cx); 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; return NULL;
if (propVal.value().isPrimitive() || if (propVal.value().isPrimitive() ||
@ -3929,7 +3934,7 @@ ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
return NULL; return NULL;
} }
return name; return JSID_TO_FLAT_STRING(nameid);
} }
// For a struct field with 'name' and 'type', add an element of the form // 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 static JSBool
AddFieldToArray(JSContext* cx, AddFieldToArray(JSContext* cx,
jsval* element, jsval* element,
JSString* name, JSFlatString* name,
JSObject* typeObj) JSObject* typeObj)
{ {
JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL); JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
@ -4048,7 +4053,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
return JS_FALSE; return JS_FALSE;
JSObject* fieldType = NULL; JSObject* fieldType = NULL;
JSString* name = ExtractStructField(cx, item.jsval_value(), &fieldType); JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
if (!name) if (!name)
return JS_FALSE; return JS_FALSE;
fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType); fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
@ -4321,7 +4326,7 @@ StructType::GetFieldInfo(JSContext* cx, JSObject* obj)
} }
const FieldInfo* 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::IsCType(cx, obj));
JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct); 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; 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) if (!field)
return JS_FALSE; return JS_FALSE;
@ -4439,7 +4444,7 @@ StructType::FieldSetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
return JS_FALSE; 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) if (!field)
return JS_FALSE; return JS_FALSE;
@ -4467,8 +4472,11 @@ StructType::AddressOfField(JSContext* cx, uintN argc, jsval* vp)
return JS_FALSE; return JS_FALSE;
} }
const FieldInfo* field = LookupField(cx, typeObj, JSFlatString *str = JS_FlattenString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
JSVAL_TO_STRING(JS_ARGV(cx, vp)[0])); if (!str)
return JS_FALSE;
const FieldInfo* field = LookupField(cx, typeObj, str);
if (!field) if (!field)
return JS_FALSE; return JS_FALSE;
@ -4611,18 +4619,23 @@ PrepareReturnType(JSContext* cx, jsval type)
return result; return result;
} }
static JS_ALWAYS_INLINE bool static JS_ALWAYS_INLINE JSBool
IsEllipsis(jsval v) IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
{ {
*isEllipsis = false;
if (!JSVAL_IS_STRING(v)) if (!JSVAL_IS_STRING(v))
return false; return true;
JSString* str = JSVAL_TO_STRING(v); JSString* str = JSVAL_TO_STRING(v);
if (str->length() != 3) if (str->length() != 3)
return true;
const jschar* chars = str->getChars(cx);
if (!chars)
return false; return false;
const jschar* chars = str->chars(), dot('.'); jschar dot = '.';
return (chars[0] == dot && *isEllipsis = (chars[0] == dot &&
chars[1] == dot && chars[1] == dot &&
chars[2] == dot); chars[2] == dot);
return true;
} }
static JSBool static JSBool
@ -4737,7 +4750,10 @@ NewFunctionInfo(JSContext* cx,
fninfo->mIsVariadic = false; fninfo->mIsVariadic = false;
for (JSUint32 i = 0; i < argLength; ++i) { 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; fninfo->mIsVariadic = true;
if (i < 1) { if (i < 1) {
JS_ReportError(cx, "\"...\" may not be the first and only parameter " JS_ReportError(cx, "\"...\" may not be the first and only parameter "

View File

@ -141,7 +141,10 @@ void
AppendString(Vector<jschar, N, AP> &v, JSString* str) AppendString(Vector<jschar, N, AP> &v, JSString* str)
{ {
JS_ASSERT(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 <size_t N, class AP> template <size_t N, class AP>
@ -154,8 +157,12 @@ AppendString(Vector<char, N, AP> &v, JSString* str)
if (!v.resize(vlen + alen)) if (!v.resize(vlen + alen))
return; return;
const jschar *chars = str->getChars(NULL);
if (!chars)
return;
for (size_t i = 0; i < alen; ++i) for (size_t i = 0; i < alen; ++i)
v[i + vlen] = char(str->chars()[i]); v[i + vlen] = char(chars[i]);
} }
template <class T, size_t N, class AP, size_t ArrayLength> template <class T, size_t N, class AP, size_t ArrayLength>
@ -186,33 +193,15 @@ PrependString(Vector<jschar, N, AP> &v, JSString* str)
if (!v.resize(vlen + alen)) if (!v.resize(vlen + alen))
return; return;
const jschar *chars = str->getChars(NULL);
if (!chars)
return;
// Move vector data forward. This is safe since we've already resized. // Move vector data forward. This is safe since we've already resized.
memmove(v.begin() + alen, v.begin(), vlen * sizeof(jschar)); memmove(v.begin() + alen, v.begin(), vlen * sizeof(jschar));
// Copy data to insert. // Copy data to insert.
memcpy(v.begin(), str->chars(), alen * sizeof(jschar)); memcpy(v.begin(), chars, alen * sizeof(jschar));
}
template <class T, size_t N, size_t M, class AP>
bool
StringsEqual(Vector<T, N, AP> &v, Vector<T, M, AP> &w)
{
if (v.length() != w.length())
return false;
return memcmp(v.begin(), w.begin(), v.length() * sizeof(T)) == 0;
}
template <size_t N, class AP>
bool
StringsEqual(Vector<jschar, N, AP> &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;
} }
/******************************************************************************* /*******************************************************************************
@ -274,7 +263,7 @@ struct FieldInfo
// Hash policy for FieldInfos. // Hash policy for FieldInfos.
struct FieldHashPolicy struct FieldHashPolicy
{ {
typedef JSString* Key; typedef JSFlatString* Key;
typedef Key Lookup; typedef Key Lookup;
static uint32 hash(const Lookup &l) { static uint32 hash(const Lookup &l) {
@ -297,7 +286,7 @@ struct FieldHashPolicy
} }
}; };
typedef HashMap<JSString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash; typedef HashMap<JSFlatString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash;
// Descriptor of ABI, return type, argument types, and variadicity for a // Descriptor of ABI, return type, argument types, and variadicity for a
// FunctionType. // FunctionType.
@ -482,7 +471,7 @@ namespace StructType {
JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj); JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj);
const FieldInfoHash* GetFieldInfo(JSContext* cx, JSObject* obj); 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); JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj);
ffi_type* BuildFFIType(JSContext* cx, JSObject* obj); ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
} }

View File

@ -133,12 +133,13 @@ Library::Create(JSContext* cx, jsval path, JSCTypesCallbacks* callbacks)
} }
PRLibSpec libSpec; PRLibSpec libSpec;
JSString* pathStr = JSVAL_TO_STRING(path); JSFlatString* pathStr = JS_FlattenString(cx, JSVAL_TO_STRING(path));
if (!pathStr)
return NULL;
#ifdef XP_WIN #ifdef XP_WIN
// On Windows, converting to native charset may corrupt path string. // On Windows, converting to native charset may corrupt path string.
// So, we have to use Unicode path directly. // So, we have to use Unicode path directly.
const PRUnichar* pathChars = reinterpret_cast<const PRUnichar*>( const PRUnichar* pathChars = JS_GetFlatStringChars(pathStr);
JS_GetStringCharsZ(cx, pathStr));
if (!pathChars) if (!pathChars)
return NULL; return NULL;

View File

@ -584,16 +584,14 @@ JS_PUBLIC_API(JSBool)
JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal) JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, JSBool *equal)
{ {
assertSameCompartment(cx, v1, v2); assertSameCompartment(cx, v1, v2);
*equal = StrictlyEqual(cx, Valueify(v1), Valueify(v2)); return StrictlyEqual(cx, Valueify(v1), Valueify(v2), equal);
return JS_TRUE;
} }
JS_PUBLIC_API(JSBool) JS_PUBLIC_API(JSBool)
JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same) JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same)
{ {
assertSameCompartment(cx, v1, v2); assertSameCompartment(cx, v1, v2);
*same = SameValue(Valueify(v1), Valueify(v2), cx); return SameValue(cx, Valueify(v1), Valueify(v2), same);
return JS_TRUE;
} }
/************************************************************************/ /************************************************************************/
@ -2239,8 +2237,14 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui
} }
case JSTRACE_STRING: 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, "<rope: length %d>", (int)str->length());
break; break;
}
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
case JSTRACE_XML: case JSTRACE_XML:
@ -5208,17 +5212,8 @@ JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
JS_PUBLIC_API(JSString *) JS_PUBLIC_API(JSString *)
JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) JS_NewStringCopyN(JSContext *cx, const char *s, size_t n)
{ {
jschar *js;
JSString *str;
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
js = js_InflateString(cx, s, &n); return js_NewStringCopyN(cx, s, n);
if (!js)
return NULL;
str = js_NewString(cx, js, n);
if (!str)
cx->free(js);
return str;
} }
JS_PUBLIC_API(JSString *) JS_PUBLIC_API(JSString *)
@ -5338,9 +5333,8 @@ JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength)
{ {
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
assertSameCompartment(cx, str); assertSameCompartment(cx, str);
const jschar *chars; *plength = str->length();
str->getCharsAndLength(chars, *plength); return str->getChars(cx);
return chars;
} }
JS_PUBLIC_API(const jschar *) JS_PUBLIC_API(const jschar *)
@ -5369,26 +5363,29 @@ JS_FlattenString(JSContext *cx, JSString *str)
extern JS_PUBLIC_API(const jschar *) extern JS_PUBLIC_API(const jschar *)
JS_GetFlatStringChars(JSFlatString *str) JS_GetFlatStringChars(JSFlatString *str)
{ {
return reinterpret_cast<JSString *>(str)->flatChars(); return str->chars();
} }
JS_PUBLIC_API(JSBool) JS_PUBLIC_API(JSBool)
JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result) JS_CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result)
{ {
*result = js_CompareStrings(str1, str2); return CompareStrings(cx, str1, str2, result);
return JS_TRUE;
} }
JS_PUBLIC_API(JSBool) JS_PUBLIC_API(JSBool)
JS_StringEqualsAscii(JSContext *cx, JSString *str, const char *asciiBytes, JSBool *match) 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_PUBLIC_API(JSBool)
JS_FlatStringEqualsAscii(JSFlatString *str, const char *asciiBytes) JS_FlatStringEqualsAscii(JSFlatString *str, const char *asciiBytes)
{ {
return MatchStringAndAscii(str, asciiBytes); return StringEqualsAscii(str, asciiBytes);
} }
JS_PUBLIC_API(size_t) 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_PUBLIC_API(size_t)
JS_PutEscapedString(JSContext *cx, char *buffer, size_t size, JSString *str, char quote) 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_PUBLIC_API(JSBool)
JS_FileEscapedString(FILE *fp, JSString *str, char quote) 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 *) 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_PUBLIC_API(char *)
JS_EncodeString(JSContext *cx, JSString *str) 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_PUBLIC_API(size_t)
JS_GetStringEncodingLength(JSContext *cx, JSString *str) 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) JS_PUBLIC_API(size_t)
@ -5488,12 +5495,15 @@ JS_EncodeStringToBuffer(JSString *str, char *buffer, size_t length)
* error. * error.
*/ */
size_t writtenLength = length; 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); JS_ASSERT(writtenLength <= length);
return writtenLength; return writtenLength;
} }
JS_ASSERT(writtenLength <= length); 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)) if (necessaryLength == size_t(-1))
return size_t(-1); return size_t(-1);
if (writtenLength != length) { if (writtenLength != length) {

View File

@ -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 * 'id' is passed as a jsboxedword since the given id need not necessarily hold
* an atomized string. * an atomized string.
*/ */
JSBool bool
js_StringIsIndex(JSString *str, jsuint *indexp) js_StringIsIndex(JSLinearString *str, jsuint *indexp)
{ {
jschar *cp = str->chars(); const jschar *cp = str->chars();
if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) { if (JS7_ISDEC(*cp) && str->length() < sizeof(MAXSTR)) {
jsuint index = JS7_UNDEC(*cp++); jsuint index = JS7_UNDEC(*cp++);
jsuint oldIndex = 0; jsuint oldIndex = 0;
@ -166,10 +166,10 @@ js_StringIsIndex(JSString *str, jsuint *indexp)
(oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))
{ {
*indexp = index; *indexp = index;
return JS_TRUE; return true;
} }
} }
return JS_FALSE; return false;
} }
static bool static bool
@ -1149,12 +1149,13 @@ array_toSource(JSContext *cx, uintN argc, Value *vp)
goto out; goto out;
} }
vp->setString(str); vp->setString(str);
const jschar *chars;
size_t charlen; const jschar *chars = str->getChars(cx);
str->getCharsAndLength(chars, charlen); if (!chars)
goto out;
/* Append element to buffer. */ /* Append element to buffer. */
if (!cb.append(chars, charlen)) if (!cb.append(chars, chars + str->length()))
goto out; goto out;
if (index + 1 != length) { if (index + 1 != length) {
if (!js_AppendLiteral(cb, ", ")) if (!js_AppendLiteral(cb, ", "))
@ -1188,6 +1189,20 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
{ {
JS_CHECK_RECURSION(cx, return false); 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 = &comma;
seplen = 1;
}
/* /*
* Use HashTable entry as the cycle indicator. On first visit, create the * Use HashTable entry as the cycle indicator. On first visit, create the
* entry, and, when leaving, remove the entry. * entry, and, when leaving, remove the entry.
@ -1197,10 +1212,8 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
uint32 genBefore; uint32 genBefore;
if (!hashp) { if (!hashp) {
/* Not in hash table, so not a cycle. */ /* Not in hash table, so not a cycle. */
if (!cx->busyArrays.add(hashp, obj)) { if (!cx->busyArrays.add(hashp, obj))
JS_ReportOutOfMemory(cx);
return false; return false;
}
genBefore = cx->busyArrays.generation(); genBefore = cx->busyArrays.generation();
} else { } else {
/* Cycle, so return empty string. */ /* Cycle, so return empty string. */
@ -1214,17 +1227,6 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
MUST_FLOW_THROUGH("out"); MUST_FLOW_THROUGH("out");
bool ok = false; 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 = &comma;
seplen = 1;
}
/* /*
* This object will take responsibility for the jschar buffer until the * This object will take responsibility for the jschar buffer until the
* buffer is transferred to the returned JSString. * buffer is transferred to the returned JSString.
@ -1710,15 +1712,10 @@ comparator_stack_cast(JSRedComparator func)
static int static int
sort_compare_strings(void *arg, const void *a, const void *b, int *result) sort_compare_strings(void *arg, const void *a, const void *b, int *result)
{ {
const Value *av = (const Value *)a, *bv = (const Value *)b; JSContext *cx = (JSContext *)arg;
JSString *astr = ((const Value *)a)->toString();
JS_ASSERT(av->isString()); JSString *bstr = ((const Value *)b)->toString();
JS_ASSERT(bv->isString()); return JS_CHECK_OPERATION_LIMIT(cx) && CompareStrings(cx, astr, bstr, result);
if (!JS_CHECK_OPERATION_LIMIT((JSContext *)arg))
return JS_FALSE;
*result = (int) js_CompareStrings(av->toString(), bv->toString());
return JS_TRUE;
} }
JSBool JSBool
@ -2615,9 +2612,14 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp)
!GetElement(cx, obj, (jsuint)i, &hole, vp)) { !GetElement(cx, obj, (jsuint)i, &hole, vp)) {
return JS_FALSE; return JS_FALSE;
} }
if (!hole && StrictlyEqual(cx, *vp, tosearch)) { if (!hole) {
vp->setNumber(i); JSBool equal;
return JS_TRUE; if (!StrictlyEqual(cx, *vp, tosearch, &equal))
return JS_FALSE;
if (equal) {
vp->setNumber(i);
return JS_TRUE;
}
} }
if (i == stop) if (i == stop)
goto not_found; goto not_found;

View File

@ -44,6 +44,7 @@
*/ */
#include "jsprvtd.h" #include "jsprvtd.h"
#include "jspubtd.h" #include "jspubtd.h"
#include "jsatom.h"
#include "jsobj.h" #include "jsobj.h"
#include "jsstr.h" #include "jsstr.h"
@ -87,8 +88,8 @@ JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra)
return growSlots(cx, requiredCapacity) ? ED_OK : ED_FAILED; return growSlots(cx, requiredCapacity) ? ED_OK : ED_FAILED;
} }
extern JSBool extern bool
js_StringIsIndex(JSString *str, jsuint *indexp); js_StringIsIndex(JSLinearString *str, jsuint *indexp);
inline JSBool inline JSBool
js_IdIsIndex(jsid id, jsuint *indexp) js_IdIsIndex(jsid id, jsuint *indexp)
@ -105,26 +106,36 @@ js_IdIsIndex(jsid id, jsuint *indexp)
if (JS_UNLIKELY(!JSID_IS_STRING(id))) if (JS_UNLIKELY(!JSID_IS_STRING(id)))
return JS_FALSE; 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. */ /* XML really wants to pretend jsvals are jsids. */
inline JSBool inline bool
js_IdValIsIndex(jsval id, jsuint *indexp) js_IdValIsIndex(JSContext *cx, jsval id, jsuint *indexp, bool *isIndex)
{ {
if (JSVAL_IS_INT(id)) { if (JSVAL_IS_INT(id)) {
jsint i; jsint i;
i = JSVAL_TO_INT(id); i = JSVAL_TO_INT(id);
if (i < 0) if (i < 0) {
return JS_FALSE; *isIndex = false;
return true;
}
*indexp = (jsuint)i; *indexp = (jsuint)i;
return JS_TRUE; *isIndex = true;
return true;
} }
if (!JSVAL_IS_STRING(id)) if (!JSVAL_IS_STRING(id)) {
return JS_FALSE; *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; extern js::Class js_ArrayClass, js_SlowArrayClass;

View File

@ -457,17 +457,20 @@ js_SweepAtomState(JSContext *cx)
} }
JSAtom * 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(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
if (str->isAtomized()) if (strArg->isAtomized())
return STRING_TO_ATOM(str); return STRING_TO_ATOM(strArg);
const jschar *chars; JSLinearString *str = strArg->ensureLinear(cx);
size_t length; if (!str)
str->getCharsAndLength(chars, length); return NULL;
const jschar *chars = str->chars();
size_t length = str->length();
JSString *staticStr = JSString::lookupStaticString(chars, length); JSString *staticStr = JSString::lookupStaticString(chars, length);
if (staticStr) 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. */ /* Hashing the string should have flattened it if it was a rope. */
JS_ASSERT(str->isFlat() || str->isDependent()); JS_ASSERT(str->isFlat() || str->isDependent());
JSString *key; JSLinearString *key;
if (p) { if (p) {
key = AtomEntryToKey(*p); key = AtomEntryToKey(*p);
} else { } else {
@ -506,9 +509,8 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
} else { } else {
if (needNewString) { if (needNewString) {
SwitchToCompartment sc(cx, cx->runtime->defaultCompartment); SwitchToCompartment sc(cx, cx->runtime->defaultCompartment);
jschar *chars = str->chars();
if (flags & ATOM_NOCOPY) { if (flags & ATOM_NOCOPY) {
key = js_NewString(cx, chars, length); key = js_NewString(cx, const_cast<jschar *>(str->flatChars()), length);
if (!key) if (!key)
return NULL; return NULL;
@ -537,7 +539,6 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED)); AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED));
JS_ASSERT(key->isAtomized());
JSAtom *atom = STRING_TO_ATOM(key); JSAtom *atom = STRING_TO_ATOM(key);
return atom; return atom;
} }
@ -607,7 +608,7 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
state = &cx->runtime->atomState; state = &cx->runtime->atomState;
JS_LOCK(cx, &state->lock); 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; str2 = p ? AtomEntryToKey(*p) : NULL;
JS_UNLOCK(cx, &state->lock); JS_UNLOCK(cx, &state->lock);
@ -628,7 +629,7 @@ js_DumpAtoms(JSContext *cx, FILE *fp)
if (entry == 0) { if (entry == 0) {
fputs("<uninitialized>", fp); fputs("<uninitialized>", fp);
} else { } else {
JSString *key = AtomEntryToKey(entry); JSAtom *key = AtomEntryToKey(entry);
FileEscapedString(fp, key, '"'); FileEscapedString(fp, key, '"');
uintN flags = AtomEntryFlags(entry); uintN flags = AtomEntryFlags(entry);
if (flags != 0) { if (flags != 0) {

View File

@ -60,7 +60,7 @@
#define STRING_TO_ATOM(str) (JS_ASSERT(str->isAtomized()), \ #define STRING_TO_ATOM(str) (JS_ASSERT(str->isAtomized()), \
(JSAtom *)str) (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)) #define ATOM_TO_JSVAL(atom) STRING_TO_JSVAL(ATOM_TO_STRING(atom))
/* Engine-internal extensions of jsid */ /* Engine-internal extensions of jsid */
@ -265,23 +265,23 @@ JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JS_GCTHING_ALIGN);
typedef uintptr_t AtomEntryType; typedef uintptr_t AtomEntryType;
static JS_ALWAYS_INLINE JSString * static JS_ALWAYS_INLINE JSAtom *
AtomEntryToKey(AtomEntryType entry) AtomEntryToKey(AtomEntryType entry)
{ {
JS_ASSERT(entry != 0); JS_ASSERT(entry != 0);
return (JSString *)(entry & ~ATOM_ENTRY_FLAG_MASK); return (JSAtom *)(entry & ~ATOM_ENTRY_FLAG_MASK);
} }
struct AtomHasher struct AtomHasher
{ {
typedef JSString *Lookup; typedef JSLinearString *Lookup;
static HashNumber hash(JSString *str) { static HashNumber hash(JSLinearString *str) {
return js_HashString(str); return js_HashString(str);
} }
static bool match(AtomEntryType entry, JSString *lookup) { static bool match(AtomEntryType entry, JSLinearString *lookup) {
return entry ? js_EqualStrings(AtomEntryToKey(entry), lookup) : false; return entry ? EqualStrings(AtomEntryToKey(entry), lookup) : false;
} }
}; };

View File

@ -168,18 +168,22 @@ js_DoubleToUint32(jsdouble d)
JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, ACCSET_NONE) JS_DEFINE_CALLINFO_1(extern, UINT32, js_DoubleToUint32, DOUBLE, 1, ACCSET_NONE)
jsdouble FASTCALL jsdouble FASTCALL
js_StringToNumber(JSContext* cx, JSString* str) js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok)
{ {
return StringToNumberType<jsdouble>(cx, str); double out = 0; /* silence warnings. */
*ok = StringToNumberType<jsdouble>(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 int32 FASTCALL
js_StringToInt32(JSContext* cx, JSString* str) js_StringToInt32(JSContext* cx, JSString* str, JSBool *ok)
{ {
return StringToNumberType<int32>(cx, str); int32 out = 0; /* silence warnings. */
*ok = StringToNumberType<int32>(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. */ /* Nb: it's always safe to set isDefinitelyAtom to false if you're unsure or don't know. */
static inline JSBool static inline JSBool

View File

@ -547,7 +547,7 @@ struct ClosureVarInfo;
#define _JS_DEFINE_CALLINFO_n(n, args) JS_DEFINE_CALLINFO_##n args #define _JS_DEFINE_CALLINFO_n(n, args) JS_DEFINE_CALLINFO_##n args
jsdouble FASTCALL jsdouble FASTCALL
js_StringToNumber(JSContext* cx, JSString* str); js_StringToNumber(JSContext* cx, JSString* str, JSBool *ok);
/* Extern version of SetBuiltinError. */ /* Extern version of SetBuiltinError. */
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
@ -623,9 +623,9 @@ JS_DECLARE_CALLINFO(js_CloneRegExpObject)
/* Defined in jsstr.cpp. */ /* Defined in jsstr.cpp. */
JS_DECLARE_CALLINFO(js_String_tn) 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_ConcatStrings)
JS_DECLARE_CALLINFO(js_EqualStrings) JS_DECLARE_CALLINFO(js_EqualStringsOnTrace)
JS_DECLARE_CALLINFO(js_Flatten) JS_DECLARE_CALLINFO(js_Flatten)
/* Defined in jstypedarray.cpp. */ /* Defined in jstypedarray.cpp. */

View File

@ -350,9 +350,10 @@ JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
bool bool
JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str) JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str)
{ {
const jschar *chars; size_t length = str->length();
size_t length; const jschar *chars = str->getChars(context());
str->getCharsAndLength(chars, length); if (!chars)
return false;
return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length); return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length);
} }
@ -750,9 +751,10 @@ JSStructuredCloneReader::startRead(Value *vp)
JSString *str = readString(nchars); JSString *str = readString(nchars);
if (!str) if (!str)
return false; return false;
const jschar *chars; size_t length = str->length();
size_t length; const jschar *chars = str->getChars(context());
str->getCharsAndLength(chars, length); if (!chars)
return false;
JSObject *obj = RegExp::createObjectNoStatics(context(), chars, length, data); JSObject *obj = RegExp::createObjectNoStatics(context(), chars, length, data);
if (!obj) if (!obj)
return false; return false;

View File

@ -1369,7 +1369,7 @@ struct JSRuntime {
js::Value negativeInfinityValue; js::Value negativeInfinityValue;
js::Value positiveInfinityValue; js::Value positiveInfinityValue;
JSString *emptyString; JSFlatString *emptyString;
/* List of active contexts sharing this runtime; protected by gcLock. */ /* List of active contexts sharing this runtime; protected by gcLock. */
JSCList contextList; JSCList contextList;
@ -2386,10 +2386,10 @@ struct JSContext
#ifdef XP_WIN #ifdef XP_WIN
volatile DollarPath *dollarPath; volatile DollarPath *dollarPath;
volatile JSSubString *sub; volatile JSSubString *sub;
volatile jschar *blackBox; volatile const jschar *blackBox;
volatile jschar **repstrChars; volatile const jschar **repstrChars;
volatile jschar **repstrDollar; volatile const jschar **repstrDollar;
volatile jschar **repstrDollarEnd; volatile const jschar **repstrDollarEnd;
volatile size_t *peekLen; volatile size_t *peekLen;
#endif #endif

View File

@ -204,7 +204,10 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
if (vp->isString()) { if (vp->isString()) {
Value orig = *vp; Value orig = *vp;
JSString *str = vp->toString(); 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) if (!wrapped)
return false; return false;
vp->setString(wrapped); vp->setString(wrapped);

View File

@ -750,7 +750,7 @@ ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
*/ */
static JSBool static JSBool
date_parseISOString(JSString *str, jsdouble *result, JSContext *cx) date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx)
{ {
jsdouble msec; jsdouble msec;
@ -792,7 +792,8 @@ date_parseISOString(JSString *str, jsdouble *result, JSContext *cx)
if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \ if (!ndigits(n, &field, s, &i, limit)) { goto syntax; } \
JS_END_MACRO JS_END_MACRO
str->getCharsAndLength(s, limit); s = str->chars();
limit = str->length();
if (PEEK('+') || PEEK('-')) { if (PEEK('+') || PEEK('-')) {
if (PEEK('-')) if (PEEK('-'))
@ -883,7 +884,7 @@ date_parseISOString(JSString *str, jsdouble *result, JSContext *cx)
} }
static JSBool static JSBool
date_parseString(JSString *str, jsdouble *result, JSContext *cx) date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx)
{ {
jsdouble msec; jsdouble msec;
@ -907,7 +908,8 @@ date_parseString(JSString *str, jsdouble *result, JSContext *cx)
if (date_parseISOString(str, result, cx)) if (date_parseISOString(str, result, cx))
return JS_TRUE; return JS_TRUE;
str->getCharsAndLength(s, limit); s = str->chars();
limit = str->length();
if (limit == 0) if (limit == 0)
goto syntax; goto syntax;
while (i < limit) { while (i < limit) {
@ -1167,7 +1169,11 @@ date_parse(JSContext *cx, uintN argc, Value *vp)
if (!str) if (!str)
return JS_FALSE; return JS_FALSE;
vp[2].setString(str); 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); vp->setDouble(js_NaN);
return true; return true;
} }
@ -2377,8 +2383,6 @@ date_toString(JSContext *cx, uintN argc, Value *vp)
static JSBool static JSBool
date_valueOf(JSContext *cx, uintN argc, Value *vp) 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 /* 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 * need to check for that explicitly here because every path calls
* GetUTCTime, which does the check. * GetUTCTime, which does the check.
@ -2389,11 +2393,14 @@ date_valueOf(JSContext *cx, uintN argc, Value *vp)
return date_getTime(cx, argc, vp); return date_getTime(cx, argc, vp);
/* Convert to number only if the hint was given, otherwise favor string. */ /* 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) if (!str)
return JS_FALSE; return JS_FALSE;
number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); JSLinearString *linear_str = str->ensureLinear(cx);
if (js_EqualStrings(str, number_str)) 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_getTime(cx, argc, vp);
return date_toString(cx, argc, vp); return date_toString(cx, argc, vp);
} }
@ -2487,8 +2494,11 @@ js_Date(JSContext *cx, uintN argc, Value *vp)
if (!str) if (!str)
return false; return false;
argv[0].setString(str); 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; d = js_NaN;
else else
d = TIMECLIP(d); d = TIMECLIP(d);

View File

@ -612,10 +612,11 @@ StackTraceToString(JSContext *cx, JSExnPrivate *priv)
#define APPEND_STRING_TO_STACK(str) \ #define APPEND_STRING_TO_STACK(str) \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
JSString *str_ = str; \ JSString *str_ = str; \
const jschar *chars_; \ size_t length_ = str_->length(); \
size_t length_; \ const jschar *chars_ = str_->getChars(cx); \
if (!chars_) \
goto bad; \
\ \
str_->getCharsAndLength(chars_, length_); \
if (length_ > stackmax - stacklen) { \ if (length_ > stackmax - stacklen) { \
void *ptr_; \ void *ptr_; \
if (stackmax >= STACK_LENGTH_LIMIT || \ if (stackmax >= STACK_LENGTH_LIMIT || \
@ -813,11 +814,17 @@ exn_toString(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE; return JS_FALSE;
if (name_length) { 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 += name_length;
*cp++ = ':'; *cp++ = ' '; *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 += message_length;
*cp = 0; *cp = 0;
@ -917,18 +924,27 @@ exn_toSource(JSContext *cx, uintN argc, Value *vp)
return false; return false;
*cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; *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 += name_length;
*cp++ = '('; *cp++ = '(';
const jschar *message_chars = message->getChars(cx);
if (!message_chars)
return false;
if (message_length != 0) { if (message_length != 0) {
js_strncpy(cp, message->chars(), message_length); js_strncpy(cp, message_chars, message_length);
cp += message_length; cp += message_length;
} }
if (filename_length != 0) { if (filename_length != 0) {
/* append filename as ``, {filename}'' */ /* append filename as ``, {filename}'' */
*cp++ = ','; *cp++ = ' '; *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; cp += filename_length;
} else { } else {
if (lineno_as_str) { if (lineno_as_str) {
@ -942,7 +958,10 @@ exn_toSource(JSContext *cx, uintN argc, Value *vp)
if (lineno_as_str) { if (lineno_as_str) {
/* append lineno as ``, {lineno_as_str}'' */ /* append lineno as ``, {lineno_as_str}'' */
*cp++ = ','; *cp++ = ' '; *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; cp += lineno_length;
} }

View File

@ -2603,7 +2603,12 @@ Function(JSContext *cx, uintN argc, Value *vp)
for (uintN i = 0; i < n; i++) { for (uintN i = 0; i < n; i++) {
JSString *arg = argv[i].toString(); JSString *arg = argv[i].toString();
size_t arg_length = arg->length(); 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; cp += arg_length;
/* Add separating comma or terminating 0. */ /* Add separating comma or terminating 0. */
@ -2690,8 +2695,11 @@ Function(JSContext *cx, uintN argc, Value *vp)
str = cx->runtime->emptyString; str = cx->runtime->emptyString;
} }
return Compiler::compileFunctionBody(cx, fun, principals, size_t length = str->length();
str->chars(), str->length(), const jschar *chars = str->getChars(cx);
if (!chars)
return JS_FALSE;
return Compiler::compileFunctionBody(cx, fun, principals, chars, length,
filename, lineno); filename, lineno);
} }

View File

@ -1767,7 +1767,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str)
JS_ASSERT(IsFinalizableStringKind(thingKind)); JS_ASSERT(IsFinalizableStringKind(thingKind));
/* A stillborn string has null chars, so is not valid. */ /* A stillborn string has null chars, so is not valid. */
jschar *chars = str->flatChars(); jschar *chars = const_cast<jschar *>(str->flatChars());
if (!chars) if (!chars)
return; return;
if (thingKind == FINALIZE_STRING) { if (thingKind == FINALIZE_STRING) {

View File

@ -362,9 +362,13 @@ GCMarker::dumpConservativeRoots()
} }
case JSTRACE_STRING: { case JSTRACE_STRING: {
JSString *str = (JSString *) i->thing; JSString *str = (JSString *) i->thing;
char buf[50]; if (str->isLinear()) {
PutEscapedString(buf, sizeof buf, str, '"'); char buf[50];
fprintf(fp, "string %s", buf); PutEscapedString(buf, sizeof buf, str->assertIsLinear(), '"');
fprintf(fp, "string %s", buf);
} else {
fprintf(fp, "rope: length %d", (int)str->length());
}
break; break;
} }
# if JS_HAS_XML_SUPPORT # if JS_HAS_XML_SUPPORT

View File

@ -1130,40 +1130,44 @@ HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
return JS_FALSE; return JS_FALSE;
} }
static JS_ALWAYS_INLINE bool
EqualObjects(JSContext *cx, JSObject *lobj, JSObject *robj)
{
return lobj == robj;
}
bool 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; Value lval = lref, rval = rref;
if (SameType(lval, rval)) { if (SameType(lval, rval)) {
if (lval.isString()) if (lval.isString())
return js_EqualStrings(lval.toString(), rval.toString()); return EqualStrings(cx, lval.toString(), rval.toString(), equal);
if (lval.isDouble()) if (lval.isDouble()) {
return JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE); *equal = JSDOUBLE_COMPARE(lval.toDouble(), ==, rval.toDouble(), JS_FALSE);
if (lval.isObject()) return true;
return EqualObjects(cx, &lval.toObject(), &rval.toObject()); }
if (lval.isUndefined()) if (lval.isObject()) {
return true; *equal = &lval.toObject() == &rval.toObject();
return lval.payloadAsRawUint32() == rval.payloadAsRawUint32(); return true;
}
if (lval.isUndefined()) {
*equal = true;
return true;
}
*equal = lval.payloadAsRawUint32() == rval.payloadAsRawUint32();
return true;
} }
if (lval.isDouble() && rval.isInt32()) { if (lval.isDouble() && rval.isInt32()) {
double ld = lval.toDouble(); double ld = lval.toDouble();
double rd = rval.toInt32(); 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()) { if (lval.isInt32() && rval.isDouble()) {
double ld = lval.toInt32(); double ld = lval.toInt32();
double rd = rval.toDouble(); 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 static inline bool
@ -1179,15 +1183,21 @@ IsNaN(const Value &v)
} }
bool bool
SameValue(const Value &v1, const Value &v2, JSContext *cx) SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same)
{ {
if (IsNegativeZero(v1)) if (IsNegativeZero(v1)) {
return IsNegativeZero(v2); *same = IsNegativeZero(v2);
if (IsNegativeZero(v2))
return false;
if (IsNaN(v1) && IsNaN(v2))
return true; 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 JSType
@ -1516,7 +1526,6 @@ js_LogOpcode(JSContext *cx)
JSStackFrame *fp; JSStackFrame *fp;
JSFrameRegs *regs; JSFrameRegs *regs;
intN ndefs, n, nuses; intN ndefs, n, nuses;
JSString *str;
JSOp op; JSOp op;
logfp = (FILE *) cx->logfp; logfp = (FILE *) cx->logfp;
@ -1562,12 +1571,13 @@ js_LogOpcode(JSContext *cx)
*/ */
fputs("<call>", logfp); fputs("<call>", logfp);
} else { } else {
str = js_ValueToString(cx, *siter); JSString *str = js_ValueToString(cx, *siter);
if (!str) { JSLinearString *linearStr = str ? str->ensureLinear(cx) : NULL;
if (!linearStr) {
fputs("<null>", logfp); fputs("<null>", logfp);
} else {
JS_ClearPendingException(cx); JS_ClearPendingException(cx);
FileEscapedString(logfp, str, 0); } else {
FileEscapedString(logfp, linearStr, 0);
} }
} }
fputc(' ', logfp); fputc(' ', logfp);
@ -3389,7 +3399,10 @@ END_CASE(JSOP_BITAND)
if (SameType(lval, rval)) { \ if (SameType(lval, rval)) { \
if (lval.isString()) { \ if (lval.isString()) { \
JSString *l = lval.toString(), *r = rval.toString(); \ 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()) { \ } else if (lval.isDouble()) { \
double l = lval.toDouble(), r = rval.toDouble(); \ double l = lval.toDouble(), r = rval.toDouble(); \
cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \ cond = JSDOUBLE_COMPARE(l, OP, r, IFNAN); \
@ -3418,7 +3431,10 @@ END_CASE(JSOP_BITAND)
DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \ DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \
if (lval.isString() && rval.isString()) { \ if (lval.isString() && rval.isString()) { \
JSString *l = lval.toString(), *r = rval.toString(); \ 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 { \ } else { \
double l, r; \ double l, r; \
if (!ValueToNumber(cx, lval, &l) || \ if (!ValueToNumber(cx, lval, &l) || \
@ -3450,7 +3466,10 @@ END_CASE(JSOP_NE)
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \
const Value &rref = regs.sp[-1]; \ const Value &rref = regs.sp[-1]; \
const Value &lref = regs.sp[-2]; \ 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--; \ regs.sp--; \
JS_END_MACRO JS_END_MACRO
@ -3511,7 +3530,10 @@ END_CASE(JSOP_CASEX)
DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
if (lval.isString() && rval.isString()) { \ if (lval.isString() && rval.isString()) { \
JSString *l = lval.toString(), *r = rval.toString(); \ 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 { \ } else { \
double l, r; \ double l, r; \
if (!ValueToNumber(cx, lval, &l) || \ if (!ValueToNumber(cx, lval, &l) || \
@ -5094,12 +5116,14 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH)
} }
if (lval.isString()) { if (lval.isString()) {
JSString *str = lval.toString(); JSLinearString *str = lval.toString()->ensureLinear(cx);
JSString *str2; if (!str)
goto error;
JSLinearString *str2;
SEARCH_PAIRS( SEARCH_PAIRS(
match = (rval.isString() && match = (rval.isString() &&
((str2 = rval.toString()) == str || ((str2 = rval.toString()->assertIsLinear()) == str ||
js_EqualStrings(str2, str))); EqualStrings(str2, str)));
) )
} else if (lval.isNumber()) { } else if (lval.isNumber()) {
double ldbl = lval.toNumber(); double ldbl = lval.toNumber();

View File

@ -1030,11 +1030,11 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
JSObject **objp, JSProperty **propp); JSObject **objp, JSProperty **propp);
extern bool 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. */ /* === except that NaN is the same as NaN and -0 is not the same as +0. */
extern bool extern bool
SameValue(const Value &v1, const Value &v2, JSContext *cx); SameValue(JSContext *cx, const Value &v1, const Value &v2, JSBool *same);
extern JSType extern JSType
TypeOfValue(JSContext *cx, const Value &v); TypeOfValue(JSContext *cx, const Value &v);

View File

@ -315,7 +315,10 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp)
str = js_ValueToString(cx, vp[2]); str = js_ValueToString(cx, vp[2]);
if (!str) if (!str)
return JS_FALSE; 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)) if (!js_strtod(cx, bp, end, &ep, &d))
return JS_FALSE; return JS_FALSE;
if (ep == bp) { if (ep == bp) {
@ -330,12 +333,15 @@ num_parseFloat(JSContext *cx, uintN argc, Value *vp)
static jsdouble FASTCALL static jsdouble FASTCALL
ParseFloat(JSContext* cx, JSString* str) ParseFloat(JSContext* cx, JSString* str)
{ {
const jschar* bp; const jschar *bp = str->getChars(cx);
const jschar* end; if (!bp) {
const jschar* ep; SetBuiltinError(cx);
jsdouble d; 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) if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp)
return js_NaN; return js_NaN;
return d; return d;
@ -451,8 +457,10 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp)
} }
/* Steps 2-5, 9-14. */ /* Steps 2-5, 9-14. */
const jschar *ws, *end; const jschar *ws = inputString->getChars(cx);
inputString->getCharsAndEnd(ws, end); if (!ws)
return false;
const jschar *end = ws + inputString->length();
jsdouble number; jsdouble number;
if (!ParseIntStringHelper(cx, ws, end, radix, stripPrefix, &number)) if (!ParseIntStringHelper(cx, ws, end, radix, stripPrefix, &number))
@ -467,8 +475,12 @@ num_parseInt(JSContext *cx, uintN argc, Value *vp)
static jsdouble FASTCALL static jsdouble FASTCALL
ParseInt(JSContext* cx, JSString* str) ParseInt(JSContext* cx, JSString* str)
{ {
const jschar *start, *end; const jschar *start = str->getChars(cx);
str->getCharsAndEnd(start, end); if (!start) {
SetBuiltinError(cx);
return js_NaN;
}
const jschar *end = start + str->length();
jsdouble d; jsdouble d;
if (!ParseIntStringHelper(cx, start, end, 0, true, &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))) (1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, nanojit::ACCSET_NONE)))
JS_DEFINE_TRCINFO_1(num_parseFloat, 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 */ #endif /* JS_TRACER */
@ -1184,6 +1196,14 @@ js_NumberToString(JSContext *cx, jsdouble d)
return js_NumberToStringWithBase(cx, d, 10); 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 JSBool JS_FASTCALL
js_NumberValueToCharBuffer(JSContext *cx, const Value &v, JSCharBuffer &cb) js_NumberValueToCharBuffer(JSContext *cx, const Value &v, JSCharBuffer &cb)
{ {
@ -1232,13 +1252,8 @@ ValueToNumberSlow(JSContext *cx, Value v, double *out)
return true; return true;
} }
skip_int_double: skip_int_double:
if (v.isString()) { if (v.isString())
jsdouble d = StringToNumberType<jsdouble>(cx, v.toString()); return StringToNumberType<jsdouble>(cx, v.toString(), out);
if (JSDOUBLE_IS_NaN(d))
break;
*out = d;
return true;
}
if (v.isBoolean()) { if (v.isBoolean()) {
if (v.toBoolean()) { if (v.toBoolean()) {
*out = 1.0; *out = 1.0;

View File

@ -206,6 +206,10 @@ js_NumberValueToCharBuffer(JSContext *cx, const js::Value &v, JSCharBuffer &cb);
namespace js { 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 * Usually a small amount of static storage is enough, but sometimes we need
* to dynamically allocate much more. This struct encapsulates that. * to dynamically allocate much more. This struct encapsulates that.
@ -643,35 +647,44 @@ template<> struct NumberTraits<jsdouble> {
}; };
template<typename T> template<typename T>
static JS_ALWAYS_INLINE T static JS_ALWAYS_INLINE bool
StringToNumberType(JSContext *cx, JSString *str) StringToNumberType(JSContext *cx, JSString *str, T *result)
{ {
if (str->length() == 1) { size_t length = str->length();
jschar c = str->chars()[0]; const jschar *chars = str->getChars(NULL);
if ('0' <= c && c <= '9') if (!chars)
return NumberTraits<T>::toSelfType(T(c - '0')); return false;
if (JS_ISSPACE(c))
return NumberTraits<T>::toSelfType(T(0)); if (length == 1) {
return NumberTraits<T>::NaN(); jschar c = chars[0];
if ('0' <= c && c <= '9') {
*result = NumberTraits<T>::toSelfType(T(c - '0'));
return true;
}
if (JS_ISSPACE(c)) {
*result = NumberTraits<T>::toSelfType(T(0));
return true;
}
*result = NumberTraits<T>::NaN();
return true;
} }
const jschar* bp; const jschar *bp = chars;
const jschar* end; const jschar *end = chars + length;
const jschar* ep;
jsdouble d;
str->getCharsAndEnd(bp, end);
bp = js_SkipWhiteSpace(bp, end); bp = js_SkipWhiteSpace(bp, end);
/* ECMA doesn't allow signed hex numbers (bug 273467). */ /* ECMA doesn't allow signed hex numbers (bug 273467). */
if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) { if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) {
/* Looks like a hex number. */ /* Looks like a hex number. */
const jschar *endptr; const jschar *endptr;
double d;
if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) || if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) ||
js_SkipWhiteSpace(endptr, end) != end) { js_SkipWhiteSpace(endptr, end) != end) {
return NumberTraits<T>::NaN(); *result = NumberTraits<T>::NaN();
return true;
} }
return NumberTraits<T>::toSelfType(d); *result = NumberTraits<T>::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 * that have made it here (which can only be negative ones) will
* be treated as 0 without consuming the 'x' by js_strtod. * be treated as 0 without consuming the 'x' by js_strtod.
*/ */
if (!js_strtod(cx, bp, end, &ep, &d) || const jschar *ep;
js_SkipWhiteSpace(ep, end) != end) { double d;
return NumberTraits<T>::NaN(); if (!js_strtod(cx, bp, end, &ep, &d) || js_SkipWhiteSpace(ep, end) != end) {
*result = NumberTraits<T>::NaN();
return true;
} }
*result = NumberTraits<T>::toSelfType(d);
return NumberTraits<T>::toSelfType(d); return true;
} }
} }

View File

@ -484,7 +484,8 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
JSProperty *prop; JSProperty *prop;
Value *val; Value *val;
JSString *gsop[2]; JSString *gsop[2];
JSString *idstr, *valstr, *str; JSString *valstr, *str;
JSLinearString *idstr;
JS_CHECK_RECURSION(cx, return JS_FALSE); 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 * Convert id to a value and then to a string. Decide early whether we
* prefer get/set or old getter/setter syntax. * prefer get/set or old getter/setter syntax.
*/ */
idstr = js_ValueToString(cx, IdToValue(id)); JSString *s = js_ValueToString(cx, IdToValue(id));
if (!idstr) { if (!s || !(idstr = s->ensureLinear(cx))) {
ok = JS_FALSE; ok = JS_FALSE;
goto error; 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 * If id is a string that's not an identifier, or if it's a negative
* integer, then it must be quoted. * integer, then it must be quoted.
*/ */
bool idIsLexicalIdentifier = !!js_IsIdentifier(idstr); bool idIsLexicalIdentifier = js_IsIdentifier(idstr);
if (JSID_IS_ATOM(id) if (JSID_IS_ATOM(id)
? !idIsLexicalIdentifier ? !idIsLexicalIdentifier
: (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
idstr = js_QuoteString(cx, idstr, jschar('\'')); s = js_QuoteString(cx, idstr, jschar('\''));
if (!idstr) { if (!s || !(idstr = s->ensureLinear(cx))) {
ok = JS_FALSE; ok = JS_FALSE;
goto error; goto error;
} }
vp->setString(idstr); /* local root */ 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++) { for (jsint j = 0; j < valcnt; j++) {
/* /*
@ -637,7 +643,12 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
goto error; goto error;
} }
localroot[j].setString(valstr); /* local root */ 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 * 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. */ /* Allocate 1 + 1 at end for closing brace and terminating 0. */
chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar)); chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar));
if (!chars) { if (!chars) {
/* Save code space on error: let JS_free ignore null vsharp. */ chars = ochars;
cx->free(vsharp); goto overflow;
js_free(ochars);
goto error;
} }
if (comma) { if (comma) {
@ -741,8 +750,10 @@ obj_toSource(JSContext *cx, uintN argc, Value *vp)
if (gsop[j]) { if (gsop[j]) {
gsoplength = gsop[j]->length(); gsoplength = gsop[j]->length();
js_strncpy(&chars[nchars], gsop[j]->chars(), const jschar *gsopchars = gsop[j]->getChars(cx);
gsoplength); if (!gsopchars)
goto overflow;
js_strncpy(&chars[nchars], gsopchars, gsoplength);
nchars += gsoplength; nchars += gsoplength;
chars[nchars++] = ' '; chars[nchars++] = ' ';
} }
@ -987,15 +998,14 @@ js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
#endif #endif
static inline JSScript ** static inline JSScript **
EvalCacheHash(JSContext *cx, JSString *str) EvalCacheHash(JSContext *cx, JSLinearString *str)
{ {
const jschar *s; const jschar *s = str->chars();
size_t n; size_t n = str->length();
uint32 h;
str->getCharsAndLength(s, n);
if (n > 100) if (n > 100)
n = 100; n = 100;
uint32 h;
for (h = 0; n; s++, n--) for (h = 0; n; s++, n--)
h = JS_ROTATE_LEFT32(h, 4) ^ *s; h = JS_ROTATE_LEFT32(h, 4) ^ *s;
@ -1005,7 +1015,7 @@ EvalCacheHash(JSContext *cx, JSString *str)
} }
static JS_ALWAYS_INLINE JSScript * 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) 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 * Get the source string passed for safekeeping in the
* atom map by the prior eval to Compiler::compileScript. * 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 * Source matches, qualify by comparing scopeobj to the
* COMPILE_N_GO-memoized parent of the first literal * 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)) if (!CheckScopeChainValidity(cx, scopeobj))
return false; return false;
const jschar *chars; JSLinearString *linearStr = str->ensureLinear(cx);
size_t length; if (!linearStr)
str->getCharsAndLength(chars, length); 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. * 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; return false;
JSScript *script = NULL; JSScript *script = NULL;
JSScript **bucket = EvalCacheHash(cx, str); JSScript **bucket = EvalCacheHash(cx, linearStr);
if (evalType == DIRECT_EVAL && caller->isFunctionFrame()) 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 * 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; uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL;
script = Compiler::compileScript(cx, scopeobj, callerFrame, script = Compiler::compileScript(cx, scopeobj, callerFrame,
principals, tcflags, principals, tcflags, chars, length,
chars, length, filename, lineno, linearStr, staticLevel);
filename, lineno, str, staticLevel);
if (!script) if (!script)
return false; return false;
} }
@ -2040,14 +2051,20 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
if (!shape->isAccessorDescriptor()) if (!shape->isAccessorDescriptor())
break; break;
if (desc.hasGet && if (desc.hasGet) {
!SameValue(desc.getterValue(), shape->getterOrUndefined(), cx)) { JSBool same;
break; if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
return JS_FALSE;
if (!same)
break;
} }
if (desc.hasSet && if (desc.hasSet) {
!SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) { JSBool same;
break; if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
return JS_FALSE;
if (!same)
break;
} }
} else { } else {
/* /*
@ -2096,8 +2113,13 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
if (!shape->isDataDescriptor()) if (!shape->isDataDescriptor())
break; break;
if (desc.hasValue && !SameValue(desc.value, v, cx)) JSBool same;
break; if (desc.hasValue) {
if (!SameValue(cx, desc.value, v, &same))
return JS_FALSE;
if (!same)
break;
}
if (desc.hasWritable && desc.writable() != shape->writable()) if (desc.hasWritable && desc.writable() != shape->writable())
break; break;
} else { } else {
@ -2144,9 +2166,14 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
/* 8.12.9 step 10. */ /* 8.12.9 step 10. */
JS_ASSERT(shape->isDataDescriptor()); JS_ASSERT(shape->isDataDescriptor());
if (!shape->configurable() && !shape->writable()) { if (!shape->configurable() && !shape->writable()) {
if ((desc.hasWritable && desc.writable()) || if (desc.hasWritable && desc.writable())
(desc.hasValue && !SameValue(desc.value, v, cx))) {
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); 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. */ /* 8.12.9 step 11. */
JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor()); JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
if (!shape->configurable()) { if (!shape->configurable()) {
if ((desc.hasSet && if (desc.hasSet) {
!SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) || JSBool same;
(desc.hasGet && if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
!SameValue(desc.getterValue(), shape->getterOrUndefined(), cx))) { return JS_FALSE;
return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval); 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)) { if (JSID_IS_INT(id)) {
JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id)); JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id));
} else if (JSID_IS_ATOM(id)) { } else if (JSID_IS_ATOM(id)) {
PutEscapedString(buf, bufsize, JSID_TO_STRING(id), 0); PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
} else { } else {
JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
} }
@ -6505,15 +6541,22 @@ js_DumpChars(const jschar *s, size_t n)
void void
dumpString(JSString *str) 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_FRIEND_API(void)
js_DumpString(JSString *str) js_DumpString(JSString *str)
{ {
fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", if (const jschar *chars = str->getChars(NULL)) {
(void *) str, (void *) str->chars()); fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",
dumpString(str); (void *) str, (void *) chars);
dumpString(str);
} else {
fprintf(stderr, "(oom in JS_DumpString)");
}
fputc('\n', stderr); fputc('\n', stderr);
} }

View File

@ -986,17 +986,21 @@ struct JSObject : js::gc::Cell {
static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3; static const uint32 NAMESPACE_CLASS_RESERVED_SLOTS = 3;
static const uint32 QNAME_CLASS_RESERVED_SLOTS = 3; static const uint32 QNAME_CLASS_RESERVED_SLOTS = 3;
inline jsval getNamePrefix() const; inline JSLinearString *getNamePrefix() const;
inline void setNamePrefix(jsval prefix); inline jsval getNamePrefixVal() const;
inline void setNamePrefix(JSLinearString *prefix);
inline void clearNamePrefix();
inline jsval getNameURI() const; inline JSLinearString *getNameURI() const;
inline void setNameURI(jsval uri); inline jsval getNameURIVal() const;
inline void setNameURI(JSLinearString *uri);
inline jsval getNamespaceDeclared() const; inline jsval getNamespaceDeclared() const;
inline void setNamespaceDeclared(jsval decl); inline void setNamespaceDeclared(jsval decl);
inline jsval getQNameLocalName() const; inline JSLinearString *getQNameLocalName() const;
inline void setQNameLocalName(jsval decl); inline jsval getQNameLocalNameVal() const;
inline void setQNameLocalName(JSLinearString *name);
/* /*
* Proxy-specific getters and setters. * Proxy-specific getters and setters.

View File

@ -530,32 +530,55 @@ JSObject::setNativeIterator(js::NativeIterator *ni)
setPrivate(ni); setPrivate(ni);
} }
inline jsval inline JSLinearString *
JSObject::getNamePrefix() const 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()); JS_ASSERT(isNamespace() || isQName());
return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX)); return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX));
} }
inline void inline void
JSObject::setNamePrefix(jsval prefix) JSObject::setNamePrefix(JSLinearString *prefix)
{ {
JS_ASSERT(isNamespace() || isQName()); 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 inline jsval
JSObject::getNameURI() const JSObject::getNameURIVal() const
{ {
JS_ASSERT(isNamespace() || isQName()); JS_ASSERT(isNamespace() || isQName());
return js::Jsvalify(getSlot(JSSLOT_NAME_URI)); return js::Jsvalify(getSlot(JSSLOT_NAME_URI));
} }
inline void inline void
JSObject::setNameURI(jsval uri) JSObject::setNameURI(JSLinearString *uri)
{ {
JS_ASSERT(isNamespace() || isQName()); JS_ASSERT(isNamespace() || isQName());
setSlot(JSSLOT_NAME_URI, js::Valueify(uri)); setSlot(JSSLOT_NAME_URI, uri ? js::StringValue(uri) : js::UndefinedValue());
} }
inline jsval inline jsval
@ -572,18 +595,26 @@ JSObject::setNamespaceDeclared(jsval decl)
setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl)); setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl));
} }
inline jsval inline JSLinearString *
JSObject::getQNameLocalName() const 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()); JS_ASSERT(isQName());
return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME)); return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME));
} }
inline void inline void
JSObject::setQNameLocalName(jsval name) JSObject::setQNameLocalName(JSLinearString *name)
{ {
JS_ASSERT(isQName()); JS_ASSERT(isQName());
setSlot(JSSLOT_QNAME_LOCAL_NAME, js::Valueify(name)); setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue());
} }
inline JSObject * inline JSObject *

View File

@ -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())) if (!JS_ConvertArguments(cx, argc, Jsvalify(argv), "S / v", &s, reviver.addr()))
return JS_FALSE; return JS_FALSE;
JSLinearString *linearStr = s->ensureLinear(cx);
if (!linearStr)
return JS_FALSE;
JSONParser *jp = js_BeginJSONParse(cx, vp); JSONParser *jp = js_BeginJSONParse(cx, vp);
JSBool ok = jp != NULL; JSBool ok = jp != NULL;
if (ok) { if (ok) {
const jschar *chars; const jschar *chars = linearStr->chars();
size_t length; size_t length = linearStr->length();
s->getCharsAndLength(chars, length);
ok = js_ConsumeJSONText(cx, jp, chars, length); ok = js_ConsumeJSONText(cx, jp, chars, length);
ok &= !!js_FinishJSONParse(cx, jp, reviver.value()); ok &= !!js_FinishJSONParse(cx, jp, reviver.value());
} }
@ -403,9 +406,11 @@ JO(JSContext *cx, Value *vp, StringifyContext *scx)
if (!s) if (!s)
return JS_FALSE; return JS_FALSE;
const jschar *chars; size_t length = s->length();
size_t length; const jschar *chars = s->getChars(cx);
s->getCharsAndLength(chars, length); if (!chars)
return JS_FALSE;
if (!write_string(cx, scx->cb, chars, length) || if (!write_string(cx, scx->cb, chars, length) ||
!scx->cb.append(':') || !scx->cb.append(':') ||
!(scx->gap.empty() || 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()) { if (vp->isString()) {
const jschar *chars; JSString *str = vp->toString();
size_t length; size_t length = str->length();
vp->toString()->getCharsAndLength(chars, length); const jschar *chars = str->getChars(cx);
if (!chars)
return JS_FALSE;
return write_string(cx, scx->cb, chars, length); return write_string(cx, scx->cb, chars, length);
} }

View File

@ -655,19 +655,16 @@ SprintCString(Sprinter *sp, const char *s)
static ptrdiff_t static ptrdiff_t
SprintString(Sprinter *sp, JSString *str) SprintString(Sprinter *sp, JSString *str)
{ {
const jschar *chars; size_t length = str->length();
size_t length, size; const jschar *chars = str->getChars(sp->context);
ptrdiff_t offset; if (!chars)
return -1;
str->getCharsAndLength(chars, length); size_t size = js_GetDeflatedStringLength(sp->context, chars, length);
if (length == 0)
return sp->offset;
size = js_GetDeflatedStringLength(sp->context, chars, length);
if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size)) if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size))
return -1; return -1;
offset = sp->offset; ptrdiff_t offset = sp->offset;
sp->offset += size; sp->offset += size;
js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset, js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset,
&size); &size);
@ -713,39 +710,36 @@ const char js_EscapeMap[] = {
static char * static char *
QuoteString(Sprinter *sp, JSString *str, uint32 quote) 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. */ /* Sample off first for later return value pointer computation. */
dontEscape = (quote & DONT_ESCAPE) != 0; JSBool dontEscape = (quote & DONT_ESCAPE) != 0;
qc = (jschar) quote; jschar qc = (jschar) quote;
off = sp->offset; ptrdiff_t off = sp->offset;
if (qc && Sprint(sp, "%c", (char)qc) < 0) if (qc && Sprint(sp, "%c", (char)qc) < 0)
return NULL; 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. */ /* Loop control variables: z points at end of string sentinel. */
str->getCharsAndEnd(s, z); for (const jschar *t = s; t < z; s = ++t) {
for (t = s; t < z; s = ++t) {
/* Move t forward from s past un-quote-worthy characters. */ /* Move t forward from s past un-quote-worthy characters. */
c = *t; jschar c = *t;
while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' && while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' &&
!(c >> 8)) { !(c >> 8)) {
c = *++t; c = *++t;
if (t == z) if (t == z)
break; break;
} }
len = t - s; ptrdiff_t len = t - s;
/* Allocate space for s, including the '\0' at the end. */ /* Allocate space for s, including the '\0' at the end. */
if (!SprintEnsureBuffer(sp, len)) if (!SprintEnsureBuffer(sp, len))
return NULL; return NULL;
/* Advance sp->offset and copy s into sp's buffer. */ /* Advance sp->offset and copy s into sp's buffer. */
bp = sp->base + sp->offset; char *bp = sp->base + sp->offset;
sp->offset += len; sp->offset += len;
while (--len >= 0) while (--len >= 0)
*bp++ = (char) *s++; *bp++ = (char) *s++;
@ -755,6 +749,8 @@ QuoteString(Sprinter *sp, JSString *str, uint32 quote)
break; break;
/* Use js_EscapeMap, \u, or \x only if necessary. */ /* Use js_EscapeMap, \u, or \x only if necessary. */
bool ok;
const char *e;
if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) { if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
ok = dontEscape ok = dontEscape
? Sprint(sp, "%c", (char)c) >= 0 ? Sprint(sp, "%c", (char)c) >= 0
@ -1580,7 +1576,6 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
const char *lval; const char *lval;
JSAtom *atom; JSAtom *atom;
jssrcnote *sn; jssrcnote *sn;
JSString *str;
JSBool hole; JSBool hole;
LOCAL_ASSERT(*pc == JSOP_DUP); LOCAL_ASSERT(*pc == JSOP_DUP);
@ -1661,18 +1656,19 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
case JSOP_GETPROP: case JSOP_GETPROP:
LOAD_ATOM(0); LOAD_ATOM(0);
do_destructure_atom: do_destructure_atom:
{
*OFF2STR(&ss->sprinter, head) = '{'; *OFF2STR(&ss->sprinter, head) = '{';
str = ATOM_TO_STRING(atom);
#if JS_HAS_DESTRUCTURING_SHORTHAND #if JS_HAS_DESTRUCTURING_SHORTHAND
nameoff = ss->sprinter.offset; nameoff = ss->sprinter.offset;
#endif #endif
if (!QuoteString(&ss->sprinter, str, if (!QuoteString(&ss->sprinter, atom,
js_IsIdentifier(str) ? 0 : (jschar)'\'')) { js_IsIdentifier(atom) ? 0 : (jschar)'\'')) {
return NULL; return NULL;
} }
if (SprintPut(&ss->sprinter, ": ", 2) < 0) if (SprintPut(&ss->sprinter, ": ", 2) < 0)
return NULL; return NULL;
break; break;
}
default: default:
LOCAL_ASSERT(0); LOCAL_ASSERT(0);
@ -5198,7 +5194,11 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in,
if (!fallback) if (!fallback)
return NULL; 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 * static char *

View File

@ -8003,11 +8003,9 @@ Parser::xmlElementOrList(JSBool allowList)
return NULL; return NULL;
} }
if (endAtom && startAtom && endAtom != startAtom) { if (endAtom && startAtom && endAtom != startAtom) {
JSString *str = ATOM_TO_STRING(startAtom);
/* End vs. start tag name mismatch: point to the tag name. */ /* End vs. start tag name mismatch: point to the tag name. */
reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH, reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH,
str->chars()); startAtom->chars());
return NULL; return NULL;
} }
@ -8676,15 +8674,12 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
if (tokenStream.matchToken(TOK_DBLCOLON)) { if (tokenStream.matchToken(TOK_DBLCOLON)) {
if (afterDot) { if (afterDot) {
JSString *str;
/* /*
* Here primaryExpr is called after . or .. followed by a name * Here primaryExpr is called after . or .. followed by a name
* followed by ::. This is the only case where a keyword after * followed by ::. This is the only case where a keyword after
* . or .. is not treated as a property name. * . or .. is not treated as a property name.
*/ */
str = ATOM_TO_STRING(pn->pn_atom); tt = js_CheckKeyword(pn->pn_atom->chars(), pn->pn_atom->length());
tt = js_CheckKeyword(str->chars(), str->length());
if (tt == TOK_FUNCTION) { if (tt == TOK_FUNCTION) {
pn->pn_arity = PN_NULLARY; pn->pn_arity = PN_NULLARY;
pn->pn_type = TOK_FUNCTION; pn->pn_type = TOK_FUNCTION;

View File

@ -480,13 +480,14 @@ Shape::dump(JSContext *cx, FILE *fp) const
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
fprintf(fp, "[%ld]", (long) JSID_TO_INT(id)); fprintf(fp, "[%ld]", (long) JSID_TO_INT(id));
} else { } else {
JSString *str; JSLinearString *str;
if (JSID_IS_ATOM(id)) { if (JSID_IS_ATOM(id)) {
str = JSID_TO_STRING(id); str = JSID_TO_ATOM(id);
} else { } else {
JS_ASSERT(JSID_IS_OBJECT(id)); JS_ASSERT(JSID_IS_OBJECT(id));
str = js_ValueToString(cx, IdToValue(id)); JSString *s = js_ValueToString(cx, IdToValue(id));
fputs("object ", fp); fputs("object ", fp);
str = s ? s->ensureLinear(cx) : NULL;
} }
if (!str) if (!str)
fputs("<error>", fp); fputs("<error>", fp);

View File

@ -92,6 +92,7 @@ typedef struct JSTreeContext JSTreeContext;
typedef struct JSTryNote JSTryNote; typedef struct JSTryNote JSTryNote;
/* Friend "Advanced API" typedefs. */ /* Friend "Advanced API" typedefs. */
typedef struct JSLinearString JSLinearString;
typedef struct JSAtom JSAtom; typedef struct JSAtom JSAtom;
typedef struct JSAtomList JSAtomList; typedef struct JSAtomList JSAtomList;
typedef struct JSAtomListElement JSAtomListElement; typedef struct JSAtomListElement JSAtomListElement;

View File

@ -2829,7 +2829,12 @@ reflect_parse(JSContext *cx, uint32 argc, jsval *vp)
if (!str) if (!str)
return JS_FALSE; 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) if (!filename)
return JS_FALSE; return JS_FALSE;
filenamep.reset(filename); filenamep.reset(filename);
@ -2844,10 +2849,10 @@ reflect_parse(JSContext *cx, uint32 argc, jsval *vp)
} }
} }
const jschar *chars; size_t length = src->length();
size_t length; const jschar *chars = src->getChars(cx);
if (!chars)
src->getCharsAndLength(chars, length); return JS_FALSE;
Parser parser(cx); Parser parser(cx);

View File

@ -243,9 +243,11 @@ RegExp::handlePCREError(JSContext *cx, int error)
bool bool
RegExp::parseFlags(JSContext *cx, JSString *flagStr, uint32 &flagsOut) RegExp::parseFlags(JSContext *cx, JSString *flagStr, uint32 &flagsOut)
{ {
const jschar *s; size_t n = flagStr->length();
size_t n; const jschar *s = flagStr->getChars(cx);
flagStr->getCharsAndLength(s, n); if (!s)
return false;
flagsOut = 0; flagsOut = 0;
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
#define HANDLE_FLAG(__name) \ #define HANDLE_FLAG(__name) \
@ -578,9 +580,12 @@ js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp)
return true; return true;
} }
const jschar *source; JSLinearString *src = re->getSource();
size_t length; size_t length = src->length();
re->getSource()->getCharsAndLength(source, length); const jschar *source = src->getChars(cx);
if (!source)
return false;
if (length == 0) { if (length == 0) {
source = empty_regexp_ucstr; source = empty_regexp_ucstr;
length = JS_ARRAY_LENGTH(empty_regexp_ucstr) - 1; length = JS_ARRAY_LENGTH(empty_regexp_ucstr) - 1;
@ -632,9 +637,11 @@ regexp_toString(JSContext *cx, uintN argc, Value *vp)
static JSString * static JSString *
EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped) EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
{ {
const jschar *oldChars; size_t oldLen = unescaped->length();
size_t oldLen; const jschar *oldChars = unescaped->getChars(cx);
unescaped->getCharsAndLength(oldChars, oldLen); if (!oldChars)
return NULL;
js::Vector<jschar, 128> newChars(cx); js::Vector<jschar, 128> newChars(cx);
for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) { for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) {
if (*it == '/' && (it == oldChars || it[-1] != '\\')) { if (*it == '/' && (it == oldChars || it[-1] != '\\')) {

View File

@ -61,7 +61,7 @@ class RegExpStatics
typedef Vector<int, 20, SystemAllocPolicy> MatchPairs; typedef Vector<int, 20, SystemAllocPolicy> MatchPairs;
MatchPairs matchPairs; MatchPairs matchPairs;
/* The input that was used to produce matchPairs. */ /* The input that was used to produce matchPairs. */
JSString *matchPairsInput; JSLinearString *matchPairsInput;
/* The input last set on the statics. */ /* The input last set on the statics. */
JSString *pendingInput; JSString *pendingInput;
uintN flags; uintN flags;
@ -180,7 +180,7 @@ class RegExpStatics
/* Mutators. */ /* Mutators. */
bool updateFromMatch(JSContext *cx, JSString *input, int *buf, size_t matchItemCount) { bool updateFromMatch(JSContext *cx, JSLinearString *input, int *buf, size_t matchItemCount) {
aboutToWrite(); aboutToWrite();
pendingInput = input; pendingInput = input;

View File

@ -76,7 +76,7 @@ regexp_statics_construct(JSContext *cx, JSObject *parent)
class RegExp class RegExp
{ {
jsrefcount refCount; jsrefcount refCount;
JSString *source; JSLinearString *source;
#if ENABLE_YARR_JIT #if ENABLE_YARR_JIT
JSC::Yarr::RegexCodeBlock compiled; JSC::Yarr::RegexCodeBlock compiled;
#else #else
@ -85,9 +85,9 @@ class RegExp
unsigned parenCount; unsigned parenCount;
uint32 flags; uint32 flags;
RegExp(JSString *source, uint32 flags) RegExp(JSLinearString *source, uint32 flags)
: refCount(1), source(source), compiled(), parenCount(0), flags(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); bool compile(JSContext *cx);
static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY; static const uint32 allFlags = JSREG_FOLD | JSREG_GLOB | JSREG_MULTILINE | JSREG_STICKY;
void handlePCREError(JSContext *cx, int error); void handlePCREError(JSContext *cx, int error);
@ -154,7 +154,7 @@ class RegExp
void decref(JSContext *cx); void decref(JSContext *cx);
/* Accessors. */ /* Accessors. */
JSString *getSource() const { return source; } JSLinearString *getSource() const { return source; }
size_t getParenCount() const { return parenCount; } size_t getParenCount() const { return parenCount; }
bool ignoreCase() const { return flags & JSREG_FOLD; } bool ignoreCase() const { return flags & JSREG_FOLD; }
bool global() const { return flags & JSREG_GLOB; } bool global() const { return flags & JSREG_GLOB; }
@ -274,7 +274,7 @@ RegExp::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemC
} }
inline bool 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) size_t *lastIndex, bool test, Value *rval)
{ {
#if !ENABLE_YARR_JIT #if !ENABLE_YARR_JIT
@ -299,8 +299,12 @@ RegExp::executeInternal(JSContext *cx, RegExpStatics *res, JSString *input,
for (int *it = buf; it != buf + matchItemCount; ++it) for (int *it = buf; it != buf + matchItemCount; ++it)
*it = -1; *it = -1;
const jschar *chars = input->chars(); JSLinearString *input = inputstr->ensureLinear(cx);
if (!input)
return false;
size_t len = input->length(); size_t len = input->length();
const jschar *chars = input->chars();
/* /*
* inputOffset emulates sticky mode by matching from this offset into the char buf and * 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 * inline RegExp *
RegExp::create(JSContext *cx, JSString *source, uint32 flags) RegExp::create(JSContext *cx, JSString *source, uint32 flags)
{ {
JSLinearString *flatSource = source->ensureLinear(cx);
if (!flatSource)
return NULL;
RegExp *self; RegExp *self;
void *mem = cx->malloc(sizeof(*self)); void *mem = cx->malloc(sizeof(*self));
if (!mem) if (!mem)
return NULL; return NULL;
self = new (mem) RegExp(source, flags); self = new (mem) RegExp(flatSource, flags);
if (!self->compile(cx)) { if (!self->compile(cx)) {
cx->destroy<RegExp>(self); cx->destroy<RegExp>(self);
return NULL; return NULL;
@ -418,7 +425,7 @@ YarrJITIsBroken(JSContext *cx)
#endif /* ANDROID */ #endif /* ANDROID */
inline bool inline bool
RegExp::compileHelper(JSContext *cx, UString &pattern) RegExp::compileHelper(JSContext *cx, JSLinearString &pattern)
{ {
#if ENABLE_YARR_JIT #if ENABLE_YARR_JIT
bool fellBack = false; bool fellBack = false;
@ -452,8 +459,13 @@ RegExp::compileHelper(JSContext *cx, UString &pattern)
inline bool inline bool
RegExp::compile(JSContext *cx) RegExp::compile(JSContext *cx)
{ {
/* Flatten source early for the rest of compilation. */
if (!source->ensureLinear(cx))
return false;
if (!sticky()) if (!sticky())
return compileHelper(cx, *source); return compileHelper(cx, *source);
/* /*
* The sticky case we implement hackily by prepending a caret onto the front * 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. * 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))) if (!cb.reserve(JS_ARRAY_LENGTH(prefix) + source->length() + JS_ARRAY_LENGTH(postfix)))
return false; return false;
JS_ALWAYS_TRUE(cb.append(prefix, JS_ARRAY_LENGTH(prefix))); 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))); JS_ALWAYS_TRUE(cb.append(postfix, JS_ARRAY_LENGTH(postfix)));
JSString *fakeySource = js_NewStringFromCharBuffer(cx, cb); JSLinearString *fakeySource = js_NewStringFromCharBuffer(cx, cb);
if (!fakeySource) if (!fakeySource)
return false; return false;
return compileHelper(cx, *fakeySource); return compileHelper(cx, *fakeySource);

View File

@ -147,19 +147,17 @@ js_CheckKeyword(const jschar *str, size_t length)
} }
JSBool JSBool
js_IsIdentifier(JSString *str) js_IsIdentifier(JSLinearString *str)
{ {
size_t length; const jschar *chars = str->chars();
jschar c; size_t length = str->length();
const jschar *chars, *end;
str->getCharsAndLength(chars, length);
if (length == 0) if (length == 0)
return JS_FALSE; return JS_FALSE;
c = *chars; jschar c = *chars;
if (!JS_ISIDSTART(c)) if (!JS_ISIDSTART(c))
return JS_FALSE; return JS_FALSE;
end = chars + length; const jschar *end = chars + length;
while (++chars != end) { while (++chars != end) {
c = *chars; c = *chars;
if (!JS_ISIDENT(c)) if (!JS_ISIDENT(c))

View File

@ -537,7 +537,7 @@ typedef void (*JSMapKeywordFun)(const char *);
* check if str is a JS keyword. * check if str is a JS keyword.
*/ */
extern JSBool extern JSBool
js_IsIdentifier(JSString *str); js_IsIdentifier(JSLinearString *str);
/* /*
* Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error * Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error

View File

@ -1453,8 +1453,8 @@ PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
name = trc->debugPrintIndex ? js_setter_str : js_getter_str; name = trc->debugPrintIndex ? js_setter_str : js_getter_str;
if (JSID_IS_ATOM(id)) { if (JSID_IS_ATOM(id)) {
n = PutEscapedString(buf, bufsize - 1, JSID_TO_STRING(id), 0); n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
if (n < bufsize - 1) if (n < bufsize)
JS_snprintf(buf + n, bufsize - n, " %s", name); JS_snprintf(buf + n, bufsize - n, " %s", name);
} else if (JSID_IS_INT(shape->id)) { } else if (JSID_IS_INT(shape->id)) {
JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name); 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_VOID(id));
JS_ASSERT(JSID_IS_ATOM(id)); JS_ASSERT(JSID_IS_ATOM(id));
n = PutEscapedString(buf, bufsize - 1, JSID_TO_STRING(id), 0); n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
if (n < bufsize - 1) if (n < bufsize)
JS_snprintf(buf + n, bufsize - n, " method"); JS_snprintf(buf + n, bufsize - n, " method");
} }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,8 @@ namespace js { namespace mjit {
class Compiler; class Compiler;
}} }}
struct JSLinearString;
/* /*
* The GC-thing "string" type. * 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 * 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. * 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 struct JSString
{ {
@ -124,7 +130,7 @@ struct JSString
*/ */
size_t lengthAndFlags; /* in all strings */ size_t lengthAndFlags; /* in all strings */
union { union {
jschar *chars; /* in non-rope strings */ const jschar *chars; /* in non-rope strings */
JSString *left; /* in rope strings */ JSString *left; /* in rope strings */
} u; } u;
union { union {
@ -211,11 +217,6 @@ struct JSString
return lengthAndFlags & ROPE_BIT; return lengthAndFlags & ROPE_BIT;
} }
JS_ALWAYS_INLINE jschar *chars() {
ensureNotRope();
return u.chars;
}
JS_ALWAYS_INLINE size_t length() const { JS_ALWAYS_INLINE size_t length() const {
return lengthAndFlags >> LENGTH_SHIFT; return lengthAndFlags >> LENGTH_SHIFT;
} }
@ -224,13 +225,18 @@ struct JSString
return lengthAndFlags <= TYPE_FLAGS_MASK; return lengthAndFlags <= TYPE_FLAGS_MASK;
} }
JS_ALWAYS_INLINE void getCharsAndLength(const jschar *&chars, size_t &length) { /* This can fail by returning null and reporting an error on cx. */
chars = this->chars(); JS_ALWAYS_INLINE const jschar *getChars(JSContext *cx) {
length = this->length(); if (isRope())
return flatten(cx);
return nonRopeChars();
} }
JS_ALWAYS_INLINE void getCharsAndEnd(const jschar *&chars, const jschar *&end) { /* This can fail by returning null and reporting an error on cx. */
end = length() + (chars = this->chars()); 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) { JS_ALWAYS_INLINE void initFlatNotTerminated(jschar *chars, size_t length) {
@ -246,7 +252,7 @@ struct JSString
JS_ASSERT(chars[length] == jschar(0)); 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(length <= MAX_LENGTH);
JS_ASSERT(chars >= inlineStorage && chars < (jschar *)(this + 2)); JS_ASSERT(chars >= inlineStorage && chars < (jschar *)(this + 2));
JS_ASSERT(!isStatic(this)); JS_ASSERT(!isStatic(this));
@ -263,7 +269,12 @@ struct JSString
s.capacity = cap; s.capacity = cap;
} }
JS_ALWAYS_INLINE jschar *flatChars() const { JS_ALWAYS_INLINE JSFlatString *assertIsFlat() {
JS_ASSERT(isFlat());
return reinterpret_cast<JSFlatString *>(this);
}
JS_ALWAYS_INLINE const jschar *flatChars() const {
JS_ASSERT(isFlat()); JS_ASSERT(isFlat());
return u.chars; return u.chars;
} }
@ -293,22 +304,22 @@ struct JSString
* The chars pointer should point somewhere inside the buffer owned by base. * The chars pointer should point somewhere inside the buffer owned by base.
* The caller still needs to pass base for GC purposes. * 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(!isStatic(this));
JS_ASSERT(base->isFlat()); JS_ASSERT(base->isFlat());
JS_ASSERT(chars >= base->chars() && chars < base->chars() + base->length()); JS_ASSERT(chars >= base->flatChars() && chars < base->flatChars() + base->length());
JS_ASSERT(length <= base->length() - (chars - base->chars())); JS_ASSERT(length <= base->length() - (chars - base->flatChars()));
lengthAndFlags = buildLengthAndFlags(length, DEPENDENT); lengthAndFlags = buildLengthAndFlags(length, DEPENDENT);
u.chars = chars; u.chars = chars;
s.base = base; s.base = base;
} }
inline JSString *dependentBase() const { inline JSLinearString *dependentBase() const {
JS_ASSERT(isDependent()); JS_ASSERT(isDependent());
return s.base; return s.base->assertIsLinear();
} }
JS_ALWAYS_INLINE jschar *dependentChars() { JS_ALWAYS_INLINE const jschar *dependentChars() {
JS_ASSERT(isDependent()); JS_ASSERT(isDependent());
return u.chars; return u.chars;
} }
@ -320,16 +331,6 @@ struct JSString
const jschar *undepend(JSContext *cx); 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 { const jschar *nonRopeChars() const {
JS_ASSERT(!isRope()); JS_ASSERT(!isRope());
return u.chars; return u.chars;
@ -353,17 +354,27 @@ struct JSString
return s.right; 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); JS_ASSERT(baseBegin <= u.chars && u.chars <= end);
lengthAndFlags = buildLengthAndFlags(end - u.chars, DEPENDENT); lengthAndFlags = buildLengthAndFlags(end - u.chars, DEPENDENT);
s.base = base; s.base = base;
} }
void flatten(); const jschar *flatten(JSContext *maybecx);
void ensureNotRope() { JSLinearString *ensureLinear(JSContext *cx) {
if (isRope()) if (isRope() && !flatten(cx))
flatten(); return NULL;
return reinterpret_cast<JSLinearString *>(this);
}
bool isLinear() const {
return !isRope();
}
JSLinearString *assertIsLinear() {
JS_ASSERT(isLinear());
return reinterpret_cast<JSLinearString *>(this);
} }
typedef uint8 SmallChar; typedef uint8 SmallChar;
@ -426,13 +437,13 @@ struct JSString
*/ */
static const JSString *const intStringTable[]; static const JSString *const intStringTable[];
static JSString *unitString(jschar c); static JSFlatString *unitString(jschar c);
static JSString *getUnitString(JSContext *cx, JSString *str, size_t index); static JSLinearString *getUnitString(JSContext *cx, JSString *str, size_t index);
static JSString *length2String(jschar c1, jschar c2); static JSFlatString *length2String(jschar c1, jschar c2);
static JSString *length2String(uint32 i); static JSFlatString *length2String(uint32 i);
static JSString *intString(jsint 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); 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)); 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 struct JSExternalString : JSString
{ {
static const uintN TYPE_LIMIT = 8; static const uintN TYPE_LIMIT = 8;
@ -548,6 +584,7 @@ namespace js {
* Implemented in jsstrinlines.h. * Implemented in jsstrinlines.h.
*/ */
class StringSegmentRange; class StringSegmentRange;
class MutatingRopeSegmentRange;
/* /*
* Utility for building a rope (lazy concatenation) of strings. * 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[]; extern const char js_encodeURIComponent_str[];
/* GC-allocate a string descriptor for the given malloc-allocated chars. */ /* GC-allocate a string descriptor for the given malloc-allocated chars. */
extern JSString * extern JSFlatString *
js_NewString(JSContext *cx, jschar *chars, size_t length); 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 * This function takes responsibility for adding the terminating '\0' required
* by js_NewString. * by js_NewString.
*/ */
extern JSString * extern JSFlatString *
js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb); js_NewStringFromCharBuffer(JSContext *cx, JSCharBuffer &cb);
extern JSString * extern JSLinearString *
js_NewDependentString(JSContext *cx, JSString *base, size_t start, js_NewDependentString(JSContext *cx, JSString *base, size_t start,
size_t length); size_t length);
/* Copy a counted string and GC-allocate a descriptor for it. */ /* 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); js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n);
extern JSString * extern JSFlatString *
js_NewStringCopyN(JSContext *cx, const char *s, size_t n); js_NewStringCopyN(JSContext *cx, const char *s, size_t n);
/* Copy a C string and GC-allocate a descriptor for it. */ /* Copy a C string and GC-allocate a descriptor for it. */
extern JSString * extern JSFlatString *
js_NewStringCopyZ(JSContext *cx, const jschar *s); js_NewStringCopyZ(JSContext *cx, const jschar *s);
extern JSString * extern JSFlatString *
js_NewStringCopyZ(JSContext *cx, const char *s); 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 * Compute a hash function from str. The caller can call this function even if
* str is not a GC-allocated thing. * str is not a GC-allocated thing.
*/ */
extern uint32 inline uint32
js_HashString(JSString *str); 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 * Test if strings are equal. The caller can call the function even if str1
* or str2 are not GC-allocated things. * or str2 are not GC-allocated things.
*/ */
extern JSBool JS_FASTCALL extern bool
js_EqualStrings(JSString *str1, JSString *str2); 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 * Return less than, equal to, or greater than zero depending on whether
* str1 is less than, equal to, or greater than str2. * str1 is less than, equal to, or greater than str2.
*/ */
extern int32 JS_FASTCALL extern bool
js_CompareStrings(JSString *str1, JSString *str2); CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32 *result);
namespace js {
/* /*
* Return true if the string matches the given sequence of ASCII bytes. * Return true if the string matches the given sequence of ASCII bytes.
*/ */
extern JSBool extern bool
MatchStringAndAscii(JSString *str, const char *asciiBytes); StringEqualsAscii(JSLinearString *str, const char *asciiBytes);
} /* namespacejs */ } /* namespacejs */
@ -994,7 +1044,7 @@ js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char);
namespace js { namespace js {
extern size_t 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 * 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. * be a single or double quote character that will quote the output.
*/ */
inline size_t 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); 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. * will quote the output.
*/ */
inline bool 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); return PutEscapedStringImpl(NULL, 0, fp, str, quote) != size_t(-1);
} }

View File

@ -42,49 +42,53 @@
#include "jsstr.h" #include "jsstr.h"
inline JSString * inline JSFlatString *
JSString::unitString(jschar c) JSString::unitString(jschar c)
{ {
JS_ASSERT(c < UNIT_STRING_LIMIT); JS_ASSERT(c < UNIT_STRING_LIMIT);
return const_cast<JSString *>(&unitStringTable[c]); return const_cast<JSString *>(&unitStringTable[c])->assertIsFlat();
} }
inline JSString * inline JSLinearString *
JSString::getUnitString(JSContext *cx, JSString *str, size_t index) JSString::getUnitString(JSContext *cx, JSString *str, size_t index)
{ {
JS_ASSERT(index < str->length()); 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) if (c < UNIT_STRING_LIMIT)
return unitString(c); return unitString(c);
return js_NewDependentString(cx, str, index, 1); return js_NewDependentString(cx, str, index, 1);
} }
inline JSString * inline JSFlatString *
JSString::length2String(jschar c1, jschar c2) JSString::length2String(jschar c1, jschar c2)
{ {
JS_ASSERT(fitsInSmallChar(c1)); JS_ASSERT(fitsInSmallChar(c1));
JS_ASSERT(fitsInSmallChar(c2)); JS_ASSERT(fitsInSmallChar(c2));
return const_cast<JSString *> return const_cast<JSString *> (
(&length2StringTable[(((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]]); &length2StringTable[(((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2]]
)->assertIsFlat();
} }
inline JSString * inline JSFlatString *
JSString::length2String(uint32 i) JSString::length2String(uint32 i)
{ {
JS_ASSERT(i < 100); JS_ASSERT(i < 100);
return length2String('0' + i / 10, '0' + i % 10); return length2String('0' + i / 10, '0' + i % 10);
} }
inline JSString * inline JSFlatString *
JSString::intString(jsint i) JSString::intString(jsint i)
{ {
jsuint u = jsuint(i); jsuint u = jsuint(i);
JS_ASSERT(u < INT_STRING_LIMIT); JS_ASSERT(u < INT_STRING_LIMIT);
return const_cast<JSString *>(JSString::intStringTable[u]); return const_cast<JSString *>(JSString::intStringTable[u])->assertIsFlat();
} }
/* Get a static atomized string for chars if possible. */ /* Get a static atomized string for chars if possible. */
inline JSString * inline JSFlatString *
JSString::lookupStaticString(const jschar *chars, size_t length) JSString::lookupStaticString(const jschar *chars, size_t length)
{ {
if (length == 1) { if (length == 1) {
@ -125,14 +129,13 @@ JSString::finalize(JSContext *cx) {
JS_ASSERT(!JSString::isStatic(this)); JS_ASSERT(!JSString::isStatic(this));
JS_RUNTIME_UNMETER(cx->runtime, liveStrings); JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
if (isDependent()) { if (isDependent()) {
JS_ASSERT(dependentBase());
JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings); JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings);
} else if (isFlat()) { } else if (isFlat()) {
/* /*
* flatChars for stillborn string is null, but cx->free checks * flatChars for stillborn string is null, but cx->free checks
* for a null pointer on its own. * for a null pointer on its own.
*/ */
cx->free(flatChars()); cx->free(const_cast<jschar *>(flatChars()));
} }
} }
@ -153,7 +156,7 @@ JSExternalString::finalize(JSContext *cx)
JS_RUNTIME_UNMETER(cx->runtime, liveStrings); JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
/* A stillborn string has null chars. */ /* A stillborn string has null chars. */
jschar *chars = flatChars(); jschar *chars = const_cast<jschar *>(flatChars());
if (!chars) if (!chars)
return; return;
JSStringFinalizeOp finalizer = str_finalizers[externalStringType]; JSStringFinalizeOp finalizer = str_finalizers[externalStringType];

View File

@ -437,14 +437,14 @@ jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
int index = -1; int index = -1;
if (JSID_IS_STRING(id)) { if (JSID_IS_STRING(id)) {
JSString* str = JSID_TO_STRING(id); JSAtom* str = JSID_TO_ATOM(id);
if (MatchStringAndAscii(str, "HOTLOOP")) { if (StringEqualsAscii(str, "HOTLOOP")) {
*vp = INT_TO_JSVAL(HOTLOOP); *vp = INT_TO_JSVAL(HOTLOOP);
return JS_TRUE; return JS_TRUE;
} }
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
if (MatchStringAndAscii(str, "profiler")) { if (StringEqualsAscii(str, "profiler")) {
*vp = BOOLEAN_TO_JSVAL(cx->profilingEnabled); *vp = BOOLEAN_TO_JSVAL(cx->profilingEnabled);
return JS_TRUE; 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. // protect against massive spew if u.s is a bad pointer.
if (length > 1 << 16) if (length > 1 << 16)
length = 1 << 16; length = 1 << 16;
jschar *chars = u.s->chars(); if (u.s->isRope()) {
fprintf(out, "<rope>");
break;
}
const jschar *chars = u.s->nonRopeChars();
for (unsigned i = 0; i < length; ++i) { for (unsigned i = 0; i < length; ++i) {
jschar co = chars[i]; jschar co = chars[i];
if (co < 128) if (co < 128)
@ -8429,8 +8433,13 @@ TraceRecorder::d2i(LIns* d, bool resultCanBeImpreciseIfFractional)
#endif #endif
} }
if (ci == &js_StringToNumber_ci) { if (ci == &js_StringToNumber_ci) {
LIns* args[] = { d->callArgN(1), d->callArgN(0) }; LIns* ok_ins = w.allocp(sizeof(JSBool));
return w.call(&js_StringToInt32_ci, args); 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 return resultCanBeImpreciseIfFractional
@ -8646,10 +8655,13 @@ TraceRecorder::switchop()
w.name(w.eqd(v_ins, w.immd(d)), "guard(switch on numeric)"), w.name(w.eqd(v_ins, w.immd(d)), "guard(switch on numeric)"),
BRANCH_EXIT); BRANCH_EXIT);
} else if (v.isString()) { } else if (v.isString()) {
LIns* args[] = { w.immpStrGC(v.toString()), v_ins }; LIns* args[] = { w.immpStrGC(v.toString()), v_ins, cx_ins };
guard(true, LIns* equal_rval = w.call(&js_EqualStringsOnTrace_ci, args);
w.name(w.eqi0(w.eqi0(w.call(&js_EqualStrings_ci, args))), guard(false,
"guard(switch on string)"), 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); BRANCH_EXIT);
} else if (v.isBoolean()) { } else if (v.isBoolean()) {
guard(true, guard(true,
@ -8711,8 +8723,12 @@ TraceRecorder::incHelper(const Value &v, LIns*& v_ins, LIns*& v_after, jsint inc
if (v.isBoolean()) { if (v.isBoolean()) {
v_ins = w.i2d(v_ins); v_ins = w.i2d(v_ins);
} else if (v.isString()) { } 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); v_ins = w.call(&js_StringToNumber_ci, args);
guard(false,
w.name(w.eqi0(w.ldiAlloc(ok_ins)), "guard(oom)"),
OOM_EXIT);
} else { } else {
JS_ASSERT(v.isNumber()); JS_ASSERT(v.isNumber());
} }
@ -8804,14 +8820,18 @@ EvalCmp(LOpcode op, double l, double r)
} }
static bool static bool
EvalCmp(LOpcode op, JSString* l, JSString* r) EvalCmp(JSContext *cx, LOpcode op, JSString* l, JSString* r, JSBool *ret)
{ {
if (op == LIR_eqd) if (op == LIR_eqd)
return !!js_EqualStrings(l, r); return EqualStrings(cx, l, r, ret);
return EvalCmp(op, js_CompareStrings(l, r), 0); 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) TraceRecorder::strictEquality(bool equal, bool cmpCase)
{ {
Value& r = stackval(-1); Value& r = stackval(-1);
@ -8819,16 +8839,21 @@ TraceRecorder::strictEquality(bool equal, bool cmpCase)
LIns* l_ins = get(&l); LIns* l_ins = get(&l);
LIns* r_ins = get(&r); LIns* r_ins = get(&r);
LIns* x; LIns* x;
bool cond; JSBool cond;
JSValueType ltag = getPromotedType(l); JSValueType ltag = getPromotedType(l);
if (ltag != getPromotedType(r)) { if (ltag != getPromotedType(r)) {
cond = !equal; cond = !equal;
x = w.immi(cond); x = w.immi(cond);
} else if (ltag == JSVAL_TYPE_STRING) { } else if (ltag == JSVAL_TYPE_STRING) {
LIns* args[] = { r_ins, l_ins }; LIns* args[] = { r_ins, l_ins, cx_ins };
x = w.eqiN(w.call(&js_EqualStrings_ci, args), equal); LIns* equal_ins = w.call(&js_EqualStringsOnTrace_ci, args);
cond = !!js_EqualStrings(l.toString(), r.toString()); 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 { } else {
if (ltag == JSVAL_TYPE_DOUBLE) if (ltag == JSVAL_TYPE_DOUBLE)
x = w.eqd(l_ins, r_ins); 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. */ /* Only guard if the same path may not always be taken. */
if (!x->isImmI()) if (!x->isImmI())
guard(cond, x, BRANCH_EXIT); guard(cond, x, BRANCH_EXIT);
return; return RECORD_CONTINUE;
} }
set(&l, x); set(&l, x);
return RECORD_CONTINUE;
} }
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
@ -8871,8 +8897,8 @@ TraceRecorder::equalityHelper(Value& l, Value& r, LIns* l_ins, LIns* r_ins,
Value& rval) Value& rval)
{ {
LOpcode op = LIR_eqi; LOpcode op = LIR_eqi;
bool cond; JSBool cond;
LIns* args[] = { NULL, NULL }; LIns* args[] = { NULL, NULL, NULL };
/* /*
* The if chain below closely mirrors that found in 11.9.3, in general * 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)); l_ins = w.getStringChar(l_ins, w.immpNonGC(0));
r_ins = w.getStringChar(r_ins, w.immpNonGC(0)); r_ins = w.getStringChar(r_ins, w.immpNonGC(0));
} else { } else {
args[0] = r_ins, args[1] = l_ins; args[0] = r_ins, args[1] = l_ins, args[2] = cx_ins;
l_ins = w.call(&js_EqualStrings_ci, args); 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); 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 { } else {
JS_ASSERT(l.isNumber() && r.isNumber()); JS_ASSERT(l.isNumber() && r.isNumber());
cond = (l.toNumber() == r.toNumber()); 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(); r_ins = w.immiUndefined();
cond = true; cond = true;
} else if (l.isNumber() && r.isString()) { } 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); 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; op = LIR_eqd;
} else if (l.isString() && r.isNumber()) { } 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); 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; op = LIR_eqd;
} else { } else {
// Below we may assign to l or r, which modifies the interpreter state. // 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& r = stackval(-1);
Value& l = stackval(-2); Value& l = stackval(-2);
LIns* x = NULL; LIns* x = NULL;
bool cond; JSBool cond;
LIns* l_ins = get(&l); LIns* l_ins = get(&l);
LIns* r_ins = get(&r); LIns* r_ins = get(&r);
bool fp = false; bool fp = false;
@ -9037,22 +9084,31 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond)
/* 11.8.5 steps 3, 16-21. */ /* 11.8.5 steps 3, 16-21. */
if (l.isString() && r.isString()) { if (l.isString() && r.isString()) {
LIns* args[] = { r_ins, l_ins }; LIns* args[] = { r_ins, l_ins, cx_ins };
l_ins = w.call(&js_CompareStrings_ci, args); 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); 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; goto do_comparison;
} }
/* 11.8.5 steps 4-5. */ /* 11.8.5 steps 4-5. */
if (!l.isNumber()) { if (!l.isNumber()) {
LIns* args[] = { l_ins, cx_ins };
if (l.isBoolean()) { if (l.isBoolean()) {
l_ins = w.i2d(l_ins); l_ins = w.i2d(l_ins);
} else if (l.isUndefined()) { } else if (l.isUndefined()) {
l_ins = w.immd(js_NaN); l_ins = w.immd(js_NaN);
} else if (l.isString()) { } 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); 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()) { } else if (l.isNull()) {
l_ins = w.immd(0.0); l_ins = w.immd(0.0);
} else { } else {
@ -9062,13 +9118,17 @@ TraceRecorder::relational(LOpcode op, bool tryBranchAfterCond)
} }
} }
if (!r.isNumber()) { if (!r.isNumber()) {
LIns* args[] = { r_ins, cx_ins };
if (r.isBoolean()) { if (r.isBoolean()) {
r_ins = w.i2d(r_ins); r_ins = w.i2d(r_ins);
} else if (r.isUndefined()) { } else if (r.isUndefined()) {
r_ins = w.immd(js_NaN); r_ins = w.immd(js_NaN);
} else if (r.isString()) { } 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); 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()) { } else if (r.isNull()) {
r_ins = w.immd(0.0); r_ins = w.immd(0.0);
} else { } else {
@ -9176,16 +9236,30 @@ TraceRecorder::binary(LOpcode op)
if (l.isString()) { if (l.isString()) {
NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() 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); 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; leftIsNumber = true;
} }
if (r.isString()) { if (r.isString()) {
NanoAssert(op != LIR_addd); // LIR_addd/IS_STRING case handled by record_JSOP_ADD() 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); 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; rightIsNumber = true;
} }
if (l.isBoolean()) { if (l.isBoolean()) {
@ -10706,8 +10780,13 @@ TraceRecorder::record_JSOP_NEG()
} }
if (v.isString()) { if (v.isString()) {
LIns* args[] = { get(&v), cx_ins }; LIns* ok_ins = w.allocp(sizeof(JSBool));
set(&v, w.negd(w.call(&js_StringToNumber_ci, args))); 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; return ARECORD_CONTINUE;
} }
@ -10739,8 +10818,13 @@ TraceRecorder::record_JSOP_POS()
} }
if (v.isString()) { if (v.isString()) {
LIns* args[] = { get(&v), cx_ins }; LIns* ok_ins = w.allocp(sizeof(JSBool));
set(&v, w.call(&js_StringToNumber_ci, args)); 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; 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); LIns *lengthAndFlags_ins = w.ldpStringLengthAndFlags(str_ins);
if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins, w.nameImmw(JSString::ROPE_BIT))))) 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); 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, if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins,
w.nameImmw(JSString::ROPE_BIT))))) 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); w.label(mbr);
} }
@ -12827,8 +12915,12 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex)
} else if (v.isUndefined()) { } else if (v.isUndefined()) {
typed_v_ins = w.immd(js_NaN); typed_v_ins = w.immd(js_NaN);
} else if (v.isString()) { } 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); 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()) { } else if (v.isBoolean()) {
JS_ASSERT(v.isBoolean()); JS_ASSERT(v.isBoolean());
typed_v_ins = w.i2d(typed_v_ins); typed_v_ins = w.i2d(typed_v_ins);
@ -14025,14 +14117,14 @@ TraceRecorder::record_JSOP_LOOKUPSWITCH()
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_STRICTEQ() TraceRecorder::record_JSOP_STRICTEQ()
{ {
strictEquality(true, false); CHECK_STATUS_A(strictEquality(true, false));
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_STRICTNE() TraceRecorder::record_JSOP_STRICTNE()
{ {
strictEquality(false, false); CHECK_STATUS_A(strictEquality(false, false));
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }
@ -14937,7 +15029,7 @@ TraceRecorder::record_JSOP_CONDSWITCH()
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_CASE() TraceRecorder::record_JSOP_CASE()
{ {
strictEquality(true, true); CHECK_STATUS_A(strictEquality(true, true));
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }
@ -15315,7 +15407,7 @@ TraceRecorder::record_JSOP_GOSUBX()
JS_REQUIRES_STACK AbortableRecordingStatus JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_CASEX() TraceRecorder::record_JSOP_CASEX()
{ {
strictEquality(true, true); CHECK_STATUS_A(strictEquality(true, true));
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }

View File

@ -1300,7 +1300,7 @@ class TraceRecorder
JS_REQUIRES_STACK RecordingStatus incElem(jsint incr, bool pre = true); JS_REQUIRES_STACK RecordingStatus incElem(jsint incr, bool pre = true);
JS_REQUIRES_STACK AbortableRecordingStatus incName(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 equality(bool negate, bool tryBranchAfterCond);
JS_REQUIRES_STACK AbortableRecordingStatus equalityHelper(Value& l, Value& r, JS_REQUIRES_STACK AbortableRecordingStatus equalityHelper(Value& l, Value& r,
nanojit::LIns* l_ins, nanojit::LIns* r_ins, nanojit::LIns* l_ins, nanojit::LIns* r_ins,

View File

@ -357,10 +357,11 @@ PodArrayZero(T (&t)[N])
template <class T> template <class T>
JS_ALWAYS_INLINE static void 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) { if (nelem < 128) {
for (T *srcend = src + nelem; src != srcend; ++src, ++dst) for (const T *srcend = src + nelem; src != srcend; ++src, ++dst)
*dst = *src; *dst = *src;
} else { } else {
memcpy(dst, src, nelem * sizeof(T)); memcpy(dst, src, nelem * sizeof(T));

View File

@ -352,7 +352,7 @@ typedef union jsval_layout
static JS_ALWAYS_INLINE JSBool static JS_ALWAYS_INLINE JSBool
JSVAL_IS_DOUBLE_IMPL(jsval_layout l) 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 static JS_ALWAYS_INLINE jsval_layout

View File

@ -265,6 +265,10 @@ class Vector : AllocPolicy
/* accessors */ /* accessors */
const AllocPolicy &allocPolicy() const {
return *this;
}
enum { InlineLength = N }; enum { InlineLength = N };
size_t length() const { size_t length() const {

View File

@ -456,13 +456,12 @@ JS_XDRString(JSXDRState *xdr, JSString **strp)
if (!JS_XDRUint32(xdr, &nchars)) if (!JS_XDRUint32(xdr, &nchars))
return JS_FALSE; return JS_FALSE;
if (xdr->mode == JSXDR_DECODE) { if (xdr->mode == JSXDR_DECODE)
chars = (jschar *) xdr->cx->malloc((nchars + 1) * sizeof(jschar)); chars = (jschar *) xdr->cx->malloc((nchars + 1) * sizeof(jschar));
if (!chars) else
return JS_FALSE; chars = const_cast<jschar *>((*strp)->getChars(xdr->cx));
} else { if (!chars)
chars = (*strp)->chars(); return JS_FALSE;
}
if (!XDRChars(xdr, chars, nchars)) if (!XDRChars(xdr, chars, nchars))
goto bad; goto bad;

File diff suppressed because it is too large Load Diff

View File

@ -312,7 +312,7 @@ js_IsXMLName(JSContext *cx, jsval v);
extern JSBool extern JSBool
js_ToAttributeName(JSContext *cx, js::Value *vp); js_ToAttributeName(JSContext *cx, js::Value *vp);
extern JSString * extern JSLinearString *
js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote); js_EscapeAttributeValue(JSContext *cx, JSString *str, JSBool quote);
extern JSString * extern JSString *

View File

@ -2830,7 +2830,8 @@ mjit::Compiler::compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const
JS_ASSERT(rhs.isPrimitive()); JS_ASSERT(rhs.isPrimitive());
if (lhs.isString() && rhs.isString()) { if (lhs.isString() && rhs.isString()) {
int cmp = js_CompareStrings(lhs.toString(), rhs.toString()); int32 cmp;
CompareStrings(cx, lhs.toString(), rhs.toString(), &cmp);
switch (op) { switch (op) {
case JSOP_LT: case JSOP_LT:
return cmp < 0; return cmp < 0;

View File

@ -1562,7 +1562,8 @@ mjit::Compiler::jsop_stricteq(JSOp op)
/* Constant-fold. */ /* Constant-fold. */
if (lhs->isConstant() && rhs->isConstant()) { 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.popn(2);
frame.push(BooleanValue((op == JSOP_STRICTEQ) ? b : !b)); frame.push(BooleanValue((op == JSOP_STRICTEQ) ? b : !b));
return; return;

View File

@ -2136,7 +2136,7 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
CodeLocationLabel cs = buffer.finalize(); CodeLocationLabel cs = buffer.finalize();
#if DEBUG #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", 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(), js_CodeName[op], cs.executableAddress(), id, chars, holder->shape(),
cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp())); cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp()));

View File

@ -940,7 +940,10 @@ template void JS_FASTCALL stubs::DefFun<false>(VMFrame &f, JSFunction *fun);
DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \
if (lval.isString() && rval.isString()) { \ if (lval.isString() && rval.isString()) { \
JSString *l = lval.toString(), *r = rval.toString(); \ 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 { \ } else { \
double l, r; \ double l, r; \
if (!ValueToNumber(cx, lval, &l) || \ if (!ValueToNumber(cx, lval, &l) || \
@ -1006,7 +1009,10 @@ StubEqualityOp(VMFrame &f)
if (lval.isString() && rval.isString()) { if (lval.isString() && rval.isString()) {
JSString *l = lval.toString(); JSString *l = lval.toString();
JSString *r = rval.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 } else
#if JS_HAS_XML_SUPPORT #if JS_HAS_XML_SUPPORT
if ((lval.isObject() && lval.toObject().isXML()) || if ((lval.isObject() && lval.toObject().isXML()) ||
@ -1066,7 +1072,10 @@ StubEqualityOp(VMFrame &f)
if (lval.isString() && rval.isString()) { if (lval.isString() && rval.isString()) {
JSString *l = lval.toString(); JSString *l = lval.toString();
JSString *r = rval.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 { } else {
double l, r; double l, r;
if (!ValueToNumber(cx, lval, &l) || if (!ValueToNumber(cx, lval, &l) ||
@ -2302,9 +2311,11 @@ stubs::StrictEq(VMFrame &f)
{ {
const Value &rhs = f.regs.sp[-1]; const Value &rhs = f.regs.sp[-1];
const Value &lhs = f.regs.sp[-2]; 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--;
f.regs.sp[-1].setBoolean(b); f.regs.sp[-1].setBoolean(equal == true);
} }
void JS_FASTCALL void JS_FASTCALL
@ -2312,9 +2323,11 @@ stubs::StrictNe(VMFrame &f)
{ {
const Value &rhs = f.regs.sp[-1]; const Value &rhs = f.regs.sp[-1];
const Value &lhs = f.regs.sp[-2]; 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--;
f.regs.sp[-1].setBoolean(b); f.regs.sp[-1].setBoolean(equal != true);
} }
void JS_FASTCALL void JS_FASTCALL
@ -2487,13 +2500,15 @@ stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
JS_ASSERT(npairs); JS_ASSERT(npairs);
if (lval.isString()) { 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++) { for (uint32 i = 1; i <= npairs; i++) {
Value rval = script->getConst(GET_INDEX(pc)); Value rval = script->getConst(GET_INDEX(pc));
pc += INDEX_LEN; pc += INDEX_LEN;
if (rval.isString()) { if (rval.isString()) {
JSString *rhs = rval.toString(); JSLinearString *rhs = rval.toString()->assertIsLinear();
if (rhs == str || js_EqualStrings(str, rhs)) { if (rhs == str || EqualStrings(str, rhs)) {
void* native = script->nativeCodeForPC(ctor, void* native = script->nativeCodeForPC(ctor,
jpc + GET_JUMP_OFFSET(pc)); jpc + GET_JUMP_OFFSET(pc));
JS_ASSERT(native); JS_ASSERT(native);

View File

@ -3402,7 +3402,12 @@ EvalInFrame(JSContext *cx, uintN argc, jsval *vp)
if (saveCurrent) if (saveCurrent)
oldfp = JS_SaveFrameChain(cx); 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, fp->script()->filename,
JS_PCToLineNumber(cx, fp->script(), JS_PCToLineNumber(cx, fp->script(),
fi.pc()), fi.pc()),

View File

@ -50,7 +50,7 @@
#include "jstl.h" #include "jstl.h"
typedef jschar UChar; typedef jschar UChar;
typedef JSString UString; typedef JSLinearString UString;
template <typename T> template <typename T>
class ValueDeleter class ValueDeleter