Bug 785551 - Do not allow inline JSString chars in the frontend; r=luke

We need direct access to jschar*s in the frontend (and some other places) for
speed, but if these pointers are inlined into JSStrings then a GC may invalidate
them.  This patch ensures that we uninline JSStrings before they enter any of
these regions.

--HG--
extra : rebase_source : ddf62ad2a47b522aa9157be8bd0643022b20909b
This commit is contained in:
Terrence Cole 2012-08-28 10:28:19 -07:00
parent 9263053a92
commit e435199147
31 changed files with 299 additions and 269 deletions

View File

@ -203,13 +203,12 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
thisv = ObjectValue(*thisobj);
}
Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx));
if (!linearStr)
Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx));
if (!stableStr)
return false;
const jschar *chars = linearStr->chars();
size_t length = linearStr->length();
SkipRoot skip(cx, &chars);
const jschar *chars = stableStr->chars();
size_t length = stableStr->length();
// If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
// Try the JSON parser first because it's much faster. If the eval string
@ -255,7 +254,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
esg.lookupInEvalCache(linearStr, caller->fun(), staticLevel);
esg.lookupInEvalCache(stableStr, caller->fun(), staticLevel);
if (!esg.foundScript()) {
unsigned lineno;
@ -272,7 +271,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *c
.setPrincipals(principals)
.setOriginPrincipals(originPrincipals);
JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller, options,
chars, length, linearStr,
chars, length, stableStr,
staticLevel);
if (!compiled)
return false;

View File

@ -4553,7 +4553,10 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsOb
return JS_FALSE;
RootedObject fieldType(cx, NULL);
JSFlatString* name = ExtractStructField(cx, item.jsval_value(), fieldType.address());
JSFlatString* flat = ExtractStructField(cx, item.jsval_value(), fieldType.address());
if (!flat)
return JS_FALSE;
Rooted<JSStableString*> name(cx, flat->ensureStable(cx));
if (!name)
return JS_FALSE;
fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);

View File

@ -197,7 +197,7 @@ CodeGenerator::visitPolyInlineDispatch(LPolyInlineDispatch *lir)
bool
CodeGenerator::visitIntToString(LIntToString *lir)
{
typedef JSFixedString *(*pf)(JSContext *, int);
typedef JSFlatString *(*pf)(JSContext *, int);
static const VMFunction IntToStringInfo = FunctionInfo<pf>(Int32ToString);
pushArg(ToRegister(lir->input()));
@ -2047,7 +2047,7 @@ CodeGenerator::visitFromCharCode(LFromCharCode *lir)
Register code = ToRegister(lir->code());
Register output = ToRegister(lir->output());
typedef JSFixedString *(*pf)(JSContext *, int32_t);
typedef JSFlatString *(*pf)(JSContext *, int32_t);
static const VMFunction Info = FunctionInfo<pf>(ion::StringFromCharCode);
OutOfLineCode *ool = oolCallVM(Info, lir, (ArgList(), code), StoreRegisterTo(output));
if (!ool)

View File

@ -330,7 +330,7 @@ ArrayShiftDense(JSContext *cx, HandleObject obj, MutableHandleValue rval)
return true;
}
JSFixedString *
JSFlatString *
StringFromCharCode(JSContext *cx, int32_t code)
{
jschar c = jschar(code);

View File

@ -174,7 +174,7 @@ template <class> struct TypeToDataType { /* Unexpected return type for a VMFunct
template <> struct TypeToDataType<bool> { static const DataType result = Type_Bool; };
template <> struct TypeToDataType<JSObject *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<JSString *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<JSFixedString *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<JSFlatString *> { static const DataType result = Type_Object; };
template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; };
template <> struct TypeToDataType<HandleString> { static const DataType result = Type_Handle; };
template <> struct TypeToDataType<HandlePropertyName> { static const DataType result = Type_Handle; };
@ -430,7 +430,7 @@ bool ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval);
bool ArrayPushDense(JSContext *cx, HandleObject obj, HandleValue v, uint32_t *length);
bool ArrayShiftDense(JSContext *cx, HandleObject obj, MutableHandleValue rval);
JSFixedString *StringFromCharCode(JSContext *cx, int32_t code);
JSFlatString *StringFromCharCode(JSContext *cx, int32_t code);
bool SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
bool strict, bool isSetName);

View File

@ -162,11 +162,9 @@ EvalScriptVersion16(JSContext *cx, unsigned argc, jsval *vp)
JS_ASSERT(argc == 1);
jsval *argv = JS_ARGV(cx, vp);
JS_ASSERT(JSVAL_IS_STRING(argv[0]));
JSString *str = JSVAL_TO_STRING(argv[0]);
const jschar *chars = str->getChars(cx);
JS_ASSERT(chars);
size_t len = str->length();
return callbackData->evalVersion(chars, len, JSVERSION_1_6);
JSStableString *str = JSVAL_TO_STRING(argv[0])->ensureStable(cx);
JS_ASSERT(str);
return callbackData->evalVersion(str->chars(), str->length(), JSVERSION_1_6);
}
JSBool

View File

@ -346,10 +346,10 @@ JS_ConvertArgumentsVA(JSContext *cx, unsigned argc, jsval *argv, const char *for
return JS_FALSE;
*sp = STRING_TO_JSVAL(str);
if (c == 'W') {
JSFixedString *fixed = str->ensureFixed(cx);
if (!fixed)
JSStableString *stable = str->ensureStable(cx);
if (!stable)
return JS_FALSE;
*va_arg(ap, const jschar **) = fixed->chars();
*va_arg(ap, const jschar **) = stable->chars();
} else {
*va_arg(ap, JSString **) = str;
}
@ -4655,7 +4655,7 @@ JS_NextProperty(JSContext *cx, JSObject *iterobjArg, jsid *idp)
iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(i));
}
}
return JS_TRUE;
return true;
}
JS_PUBLIC_API(JSBool)
@ -6094,41 +6094,60 @@ JS_GetStringCharsZ(JSContext *cx, JSString *str)
AssertHeapIsIdleOrStringIsFlat(cx, str);
CHECK_REQUEST(cx);
assertSameCompartment(cx, str);
return str->getCharsZ(cx);
JSStableString *stable = str->ensureStable(cx);
if (!stable)
return NULL;
return stable->chars();
}
JS_PUBLIC_API(const jschar *)
JS_GetStringCharsZAndLength(JSContext *cx, JSString *str, size_t *plength)
{
JS_ASSERT(plength);
AssertHeapIsIdleOrStringIsFlat(cx, str);
CHECK_REQUEST(cx);
assertSameCompartment(cx, str);
*plength = str->length();
return str->getCharsZ(cx);
JSStableString *stable = str->ensureStable(cx);
if (!stable)
return NULL;
*plength = stable->length();
return stable->chars();
}
JS_PUBLIC_API(const jschar *)
JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *plength)
{
JS_ASSERT(plength);
AssertHeapIsIdleOrStringIsFlat(cx, str);
CHECK_REQUEST(cx);
assertSameCompartment(cx, str);
*plength = str->length();
return str->getChars(cx);
JSStableString *stable = str->ensureStable(cx);
if (!stable)
return NULL;
*plength = stable->length();
return stable->chars();
}
JS_PUBLIC_API(const jschar *)
JS_GetInternedStringChars(JSString *str)
{
return str->asAtom().chars();
JS_ASSERT(str->isAtom());
JSStableString *stable = str->ensureStable(NULL);
if (!stable)
return NULL;
return stable->chars();
}
JS_PUBLIC_API(const jschar *)
JS_GetInternedStringCharsAndLength(JSString *str, size_t *plength)
{
JSAtom &atom = str->asAtom();
*plength = atom.length();
return atom.chars();
JS_ASSERT(str->isAtom());
JS_ASSERT(plength);
JSStableString *stable = str->ensureStable(NULL);
if (!stable)
return NULL;
*plength = stable->length();
return stable->chars();
}
extern JS_PUBLIC_API(JSFlatString *)
@ -6137,12 +6156,18 @@ JS_FlattenString(JSContext *cx, JSString *str)
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, str);
return str->getCharsZ(cx) ? (JSFlatString *)str : NULL;
JSFlatString *flat = str->ensureFlat(cx);
if (!flat)
return NULL;
return flat;
}
extern JS_PUBLIC_API(const jschar *)
JS_GetFlatStringChars(JSFlatString *str)
{
JSStableString *stable = str->ensureStable(NULL);
if (!stable)
return NULL;
return str->chars();
}

View File

@ -263,18 +263,16 @@ AtomizeInline(JSContext *cx, const jschar **pchars, size_t length,
/* Workaround for hash values in AddPtr being inadvertently poisoned. */
SkipRoot skip2(cx, &p);
JSFixedString *key;
JSFlatString *key;
if (ocb == TakeCharOwnership) {
key = js_NewString(cx, const_cast<jschar *>(chars), length);
if (!key)
return NULL;
*pchars = NULL; /* Called should not free *pchars. */
} else {
JS_ASSERT(ocb == CopyChars);
key = js_NewStringCopyN(cx, chars, length);
if (!key)
return NULL;
}
if (!key)
return NULL;
/*
* We have to relookup the key as the last ditch GC invoked from the
@ -310,11 +308,12 @@ js::AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
return &atom;
}
size_t length = str->length();
const jschar *chars = str->getChars(cx);
if (!chars)
JSStableString *stable = str->ensureStable(cx);
if (!stable)
return NULL;
const jschar *chars = stable->chars();
size_t length = stable->length();
JS_ASSERT(length <= JSString::MAX_LENGTH);
return AtomizeInline(cx, &chars, length, ib);
}

View File

@ -803,11 +803,12 @@ JSStructuredCloneReader::startRead(Value *vp)
JSString *str = readString(nchars);
if (!str)
return false;
size_t length = str->length();
const jschar *chars = str->getChars(context());
if (!chars)
JSStableString *stable = str->ensureStable(context());
if (!stable)
return false;
size_t length = stable->length();
const jschar *chars = stable->chars();
RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars, length, flags, NULL);
if (!reobj)
return false;

View File

@ -178,15 +178,16 @@ struct ConservativeGCData
class SourceDataCache
{
typedef HashMap<ScriptSource *,
JSFixedString *,
JSStableString *,
DefaultHasher<ScriptSource *>,
SystemAllocPolicy> Map;
Map *map_;
public:
Map *map_;
public:
SourceDataCache() : map_(NULL) {}
JSFixedString *lookup(ScriptSource *ss);
void put(ScriptSource *ss, JSFixedString *);
void purge();
JSStableString *lookup(ScriptSource *ss);
void put(ScriptSource *ss, JSStableString *);
void purge();
};
struct EvalCacheLookup

View File

@ -284,11 +284,10 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
if (vp->isString()) {
RootedValue orig(cx, *vp);
JSString *str = vp->toString();
const jschar *chars = str->getChars(cx);
if (!chars)
JSStableString *str = vp->toString()->ensureStable(cx);
if (!str)
return false;
JSString *wrapped = js_NewStringCopyN(cx, chars, str->length());
JSString *wrapped = js_NewStringCopyN(cx, str->chars(), str->length());
if (!wrapped)
return false;
vp->setString(wrapped);

View File

@ -36,21 +36,21 @@ namespace ion {
class DtoaCache {
double d;
int base;
JSFixedString *s; // if s==NULL, d and base are not valid
JSFlatString *s; // if s==NULL, d and base are not valid
public:
DtoaCache() : s(NULL) {}
void purge() { s = NULL; }
JSFixedString *lookup(int base, double d) {
JSFlatString *lookup(int base, double d) {
return this->s && base == this->base && d == this->d ? this->s : NULL;
}
void cache(int base, double d, JSFixedString *s) {
void cache(int base, double d, JSFlatString *s) {
this->base = base;
this->d = d;
this->s = s;
}
};
/* If HashNumber grows, need to change WrapperHasher. */

View File

@ -1135,8 +1135,8 @@ js_ReportUncaughtException(JSContext *cx)
report.exnType = int16_t(JSEXN_NONE);
report.column = (unsigned) column;
if (str) {
if (JSFixedString *fixed = str->ensureFixed(cx))
report.ucmessage = fixed->chars();
if (JSStableString *stable = str->ensureStable(cx))
report.ucmessage = stable->chars();
}
}

View File

@ -635,14 +635,15 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
}
if (haveSource) {
RootedScript script(cx, fun->script());
RootedString src(cx, fun->script()->sourceData(cx));
RootedString srcStr(cx, fun->script()->sourceData(cx));
if (!srcStr)
return NULL;
Rooted<JSStableString *> src(cx, srcStr->ensureStable(cx));
if (!src)
return NULL;
const jschar *chars = src->getChars(cx);
if (!chars)
return NULL;
bool exprBody = fun->flags & JSFUN_EXPR_CLOSURE;
const jschar *chars = src->chars();
bool exprBody = fun->flags & JSFUN_EXPR_CLOSURE;
// The source data for functions created by calling the Function
// constructor is only the function's body.
bool funCon = script->sourceStart == 0 && script->scriptSource()->argumentsNotIncluded();
@ -1367,19 +1368,19 @@ Function(JSContext *cx, unsigned argc, Value *vp)
const jschar *chars;
size_t length;
SkipRoot skip(cx, &chars);
if (args.length()) {
JSString *str = ToString(cx, args[args.length() - 1]);
if (!str)
return false;
strAnchor.set(str);
chars = str->getChars(cx);
length = str->length();
} else {
chars = cx->runtime->emptyString->chars();
length = 0;
}
JSString *str;
if (!args.length())
str = cx->runtime->emptyString;
else
str = ToString(cx, args[args.length() - 1]);
if (!str)
return false;
JSStableString *stable = str->ensureStable(cx);
if (!stable)
return false;
strAnchor.set(str);
chars = stable->chars();
length = stable->length();
/*
* NB: (new Function) is not lexically closed by its caller, it's just an

View File

@ -517,7 +517,7 @@ ToCStringBuf::~ToCStringBuf()
js_free(dbuf);
}
JSFixedString *
JSFlatString *
js::Int32ToString(JSContext *cx, int32_t si)
{
uint32_t ui;
@ -531,7 +531,7 @@ js::Int32ToString(JSContext *cx, int32_t si)
}
JSCompartment *c = cx->compartment;
if (JSFixedString *str = c->dtoaCache.lookup(10, si))
if (JSFlatString *str = c->dtoaCache.lookup(10, si))
return str;
JSShortString *str = js_NewGCShortString(cx);
@ -1268,7 +1268,7 @@ js_NumberToStringWithBase(JSContext *cx, double d, int base)
cbuf.dbuf && cbuf.dbuf == numStr);
}
JSFixedString *s = js_NewStringCopyZ(cx, numStr);
JSFlatString *s = js_NewStringCopyZ(cx, numStr);
c->dtoaCache.cache(base, d, s);
return s;
}
@ -1281,22 +1281,22 @@ js_NumberToString(JSContext *cx, double d)
namespace js {
JSFixedString *
JSFlatString *
NumberToString(JSContext *cx, double d)
{
if (JSString *str = js_NumberToStringWithBase(cx, d, 10))
return &str->asFixed();
return &str->asFlat();
return NULL;
}
JSFixedString *
JSFlatString *
IndexToString(JSContext *cx, uint32_t index)
{
if (StaticStrings::hasUint(index))
return cx->runtime->staticStrings.getUint(index);
JSCompartment *c = cx->compartment;
if (JSFixedString *str = c->dtoaCache.lookup(10, index))
if (JSFlatString *str = c->dtoaCache.lookup(10, index))
return str;
JSShortString *str = js_NewGCShortString(cx);

View File

@ -42,7 +42,6 @@ extern const char js_parseFloat_str[];
extern const char js_parseInt_str[];
class JSString;
class JSFixedString;
/*
* When base == 10, this function implements ToString() as specified by
@ -54,7 +53,7 @@ js_NumberToString(JSContext *cx, double d);
namespace js {
extern JSFixedString *
extern JSFlatString *
Int32ToString(JSContext *cx, int32_t i);
/*
@ -65,10 +64,10 @@ extern bool JS_FASTCALL
NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb);
/* Same as js_NumberToString, different signature. */
extern JSFixedString *
extern JSFlatString *
NumberToString(JSContext *cx, double d);
extern JSFixedString *
extern JSFlatString *
IndexToString(JSContext *cx, uint32_t index);
/*

View File

@ -59,23 +59,20 @@ js_json_parse(JSContext *cx, unsigned argc, Value *vp)
CallArgs args = CallArgsFromVp(argc, vp);
/* Step 1. */
JSLinearString *linear;
if (argc >= 1) {
JSString *str = ToString(cx, args[0]);
if (!str)
return false;
linear = str->ensureLinear(cx);
if (!linear)
return false;
} else {
linear = cx->names().undefined;
}
JS::Anchor<JSString *> anchor(linear);
JSString *str = (argc >= 1) ? ToString(cx, args[0]) : cx->names().undefined;
if (!str)
return false;
JSStableString *stable = str->ensureStable(cx);
if (!stable)
return false;
JS::Anchor<JSString *> anchor(stable);
RootedValue reviver(cx, (argc >= 2) ? args[1] : UndefinedValue());
/* Steps 2-5. */
return ParseJSONWithReviver(cx, linear->chars(), linear->length(), reviver, args.rval());
return ParseJSONWithReviver(cx, stable->chars(), stable->length(), reviver, args.rval());
}
/* ES5 15.12.3. */

View File

@ -30,9 +30,6 @@ class JSONParser
mozilla::RangedPtr<const jschar> current;
const mozilla::RangedPtr<const jschar> end;
/* For current/end as cursors into a string. */
js::SkipRoot root;
js::Value v;
const ParsingMode parsingMode;
@ -64,7 +61,6 @@ class JSONParser
: cx(cx),
current(data, length),
end(data + length, data, length),
root(cx, thisDuringConstruction()),
parsingMode(parsingMode),
errorHandling(errorHandling)
#ifdef DEBUG

View File

@ -79,7 +79,6 @@ class JSDependentString;
class JSExtensibleString;
class JSExternalString;
class JSLinearString;
class JSFixedString;
class JSRope;
class JSAtom;
class JSWrapper;

View File

@ -3478,11 +3478,12 @@ reflect_parse(JSContext *cx, uint32_t argc, jsval *vp)
if (!serialize.init(builder))
return JS_FALSE;
size_t length = src->length();
const jschar *chars = src->getChars(cx);
if (!chars)
JSStableString *stable = src->ensureStable(cx);
if (!stable)
return JS_FALSE;
const jschar *chars = stable->chars();
size_t length = stable->length();
CompileOptions options(cx);
options.setFileAndLine(filename, lineno);
Parser parser(cx, options, chars, length, /* foldConstants = */ false);

View File

@ -1047,14 +1047,14 @@ JSScript::loadSource(JSContext *cx, bool *worked)
return true;
}
JSFixedString *
JSFlatString *
JSScript::sourceData(JSContext *cx)
{
JS_ASSERT(scriptSource_->hasSourceData());
return scriptSource_->substring(cx, sourceStart, sourceEnd);
}
JSFixedString *
JSStableString *
SourceDataCache::lookup(ScriptSource *ss)
{
if (!map_)
@ -1065,7 +1065,7 @@ SourceDataCache::lookup(ScriptSource *ss)
}
void
SourceDataCache::put(ScriptSource *ss, JSFixedString *str)
SourceDataCache::put(ScriptSource *ss, JSStableString *str)
{
if (!map_) {
map_ = js_new<Map>();
@ -1087,13 +1087,13 @@ SourceDataCache::purge()
map_ = NULL;
}
JSFixedString *
JSFlatString *
ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
{
JS_ASSERT(ready());
const jschar *chars;
#if USE_ZLIB
Rooted<JSFixedString *> cached(cx, NULL);
Rooted<JSStableString *> cached(cx, NULL);
if (compressed()) {
cached = cx->runtime->sourceDataCache.lookup(this);
if (!cached) {
@ -1115,7 +1115,7 @@ ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
}
cx->runtime->sourceDataCache.put(this, cached);
}
chars = cached->getChars(cx);
chars = cached->chars();
JS_ASSERT(chars);
} else {
chars = data.source;
@ -1141,7 +1141,7 @@ ScriptSource::setSourceCopy(JSContext *cx, const jschar *src, uint32_t length,
#ifdef JS_THREADSAFE
if (tok) {
#ifdef DEBUG
ready_ = false;
ready_ = false;
#endif
tok->ss = this;
tok->chars = src;

View File

@ -559,7 +559,7 @@ struct JSScript : public js::gc::Cell
JSFunction *function() const { return function_; }
void setFunction(JSFunction *fun);
JSFixedString *sourceData(JSContext *cx);
JSFlatString *sourceData(JSContext *cx);
bool loadSource(JSContext *cx, bool *worked);
@ -1033,7 +1033,7 @@ struct ScriptSource
JS_ASSERT(hasSourceData());
return argumentsNotIncluded_;
}
JSFixedString *substring(JSContext *cx, uint32_t start, uint32_t stop);
JSFlatString *substring(JSContext *cx, uint32_t start, uint32_t stop);
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
// XDR handling

View File

@ -2438,11 +2438,11 @@ js::str_replace(JSContext *cx, unsigned argc, Value *vp)
return false;
/* We're about to store pointers into the middle of our string. */
JSFixedString *fixed = rdata.repstr->ensureFixed(cx);
if (!fixed)
JSStableString *stable = rdata.repstr->ensureStable(cx);
if (!stable)
return false;
rdata.dollarEnd = fixed->chars() + fixed->length();
rdata.dollar = js_strchr_limit(fixed->chars(), '$', rdata.dollarEnd);
rdata.dollarEnd = stable->chars() + stable->length();
rdata.dollar = js_strchr_limit(stable->chars(), '$', rdata.dollarEnd);
}
/*
@ -2991,7 +2991,7 @@ tagify(JSContext *cx, const char *begin, JSLinearString *param, const char *end,
sb.infallibleAppend('>');
JSFixedString *retstr = sb.finishString();
JSFlatString *retstr = sb.finishString();
if (!retstr)
return false;
@ -3270,10 +3270,10 @@ js_InitStringClass(JSContext *cx, JSObject *obj)
return proto;
}
JSFixedString *
JSStableString *
js_NewString(JSContext *cx, jschar *chars, size_t length)
{
JSFixedString *s = JSFixedString::new_(cx, chars, length);
JSStableString *s = JSStableString::new_(cx, chars, length);
if (s)
Probes::createString(cx, s, length);
return s;
@ -3333,7 +3333,7 @@ js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t len
return s;
}
JSFixedString *
JSFlatString *
js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
{
if (JSShortString::lengthFits(n))
@ -3344,13 +3344,13 @@ js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n)
return NULL;
js_strncpy(news, s, n);
news[n] = 0;
JSFixedString *str = js_NewString(cx, news, n);
JSFlatString *str = js_NewString(cx, news, n);
if (!str)
js_free(news);
return str;
}
JSFixedString *
JSFlatString *
js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
{
if (JSShortString::lengthFits(n))
@ -3359,13 +3359,13 @@ js_NewStringCopyN(JSContext *cx, const char *s, size_t n)
jschar *chars = InflateString(cx, s, &n);
if (!chars)
return NULL;
JSFixedString *str = js_NewString(cx, chars, n);
JSFlatString *str = js_NewString(cx, chars, n);
if (!str)
js_free(chars);
return str;
}
JSFixedString *
JSFlatString *
js_NewStringCopyZ(JSContext *cx, const jschar *s)
{
size_t n = js_strlen(s);
@ -3377,13 +3377,13 @@ js_NewStringCopyZ(JSContext *cx, const jschar *s)
if (!news)
return NULL;
js_memcpy(news, s, m);
JSFixedString *str = js_NewString(cx, news, n);
JSFlatString *str = js_NewString(cx, news, n);
if (!str)
js_free(news);
return str;
}
JSFixedString *
JSFlatString *
js_NewStringCopyZ(JSContext *cx, const char *s)
{
return js_NewStringCopyN(cx, s, strlen(s));

View File

@ -10,13 +10,15 @@
#include <ctype.h>
#include "jsapi.h"
#include "jsatom.h"
#include "jsprvtd.h"
#include "jslock.h"
#include "jsutil.h"
#include "js/HashTable.h"
#include "vm/Unicode.h"
class JSFlatString;
class JSStableString;
namespace js {
/* Implemented in jsstrinlines.h */
@ -79,24 +81,24 @@ extern const char js_decodeURIComponent_str[];
extern const char js_encodeURIComponent_str[];
/* GC-allocate a string descriptor for the given malloc-allocated chars. */
extern JSFixedString *
extern JSStableString *
js_NewString(JSContext *cx, jschar *chars, size_t length);
extern JSLinearString *
js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length);
/* Copy a counted string and GC-allocate a descriptor for it. */
extern JSFixedString *
extern JSFlatString *
js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n);
extern JSFixedString *
extern JSFlatString *
js_NewStringCopyN(JSContext *cx, const char *s, size_t n);
/* Copy a C string and GC-allocate a descriptor for it. */
extern JSFixedString *
extern JSFlatString *
js_NewStringCopyZ(JSContext *cx, const jschar *s);
extern JSFixedString *
extern JSFlatString *
js_NewStringCopyZ(JSContext *cx, const char *s);
/*

View File

@ -1180,7 +1180,10 @@ ParseNodeToQName(Parser *parser, ParseNode *pn,
JSLinearString *nsprefix;
JS_ASSERT(pn->isArity(PN_NULLARY));
JSAtom *str = pn->pn_atom;
JSAtom *atom = pn->pn_atom;
JSStableString *str = atom->ensureStable(cx);
if (!str)
return NULL;
start = str->chars();
length = str->length();
JS_ASSERT(length != 0 && *start != '@');
@ -1258,7 +1261,7 @@ ParseNodeToQName(Parser *parser, ParseNode *pn,
}
prefix = uri->empty() ? parser->context->runtime->emptyString : NULL;
}
localName = str;
localName = atom;
}
return NewXMLQName(parser->context, uri, prefix, localName);

View File

@ -3444,8 +3444,8 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName,
fullMethodName, "string", InformalValueTypeName(code));
return false;
}
Rooted<JSLinearString*> linearStr(cx, code.toString()->ensureLinear(cx));
if (!linearStr)
Rooted<JSStableString *> stable(cx, code.toString()->ensureStable(cx));
if (!stable)
return false;
/*
@ -3474,7 +3474,6 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName,
}
}
Maybe<AutoCompartment> ac;
if (fp)
ac.construct(cx, fp->scopeChain());
@ -3505,8 +3504,8 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName,
/* Run the code and produce the completion value. */
Value rval;
JS::Anchor<JSString *> anchor(linearStr);
bool ok = EvaluateInEnv(cx, env, fp, linearStr->chars(), linearStr->length(),
JS::Anchor<JSString *> anchor(stable);
bool ok = EvaluateInEnv(cx, env, fp, stable->chars(), stable->length(),
"debugger eval code", 1, &rval);
return dbg->receiveCompletionValue(ac, ok, rval, vp);
}

View File

@ -21,7 +21,7 @@
namespace js {
static JS_ALWAYS_INLINE JSFixedString *
static JS_ALWAYS_INLINE JSInlineString *
NewShortString(JSContext *cx, const jschar *chars, size_t length)
{
SkipRoot skip(cx, &chars);
@ -211,34 +211,34 @@ JSFlatString::toPropertyName(JSContext *cx)
return atom->asPropertyName();
}
JS_ALWAYS_INLINE JSAtom *
JSFlatString::morphAtomizedStringIntoAtom()
{
d.lengthAndFlags = buildLengthAndFlags(length(), ATOM_BIT);
return &asAtom();
}
JS_ALWAYS_INLINE void
JSFixedString::init(const jschar *chars, size_t length)
JSStableString::init(const jschar *chars, size_t length)
{
d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
d.u1.chars = chars;
}
JS_ALWAYS_INLINE JSFixedString *
JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length)
JS_ALWAYS_INLINE JSStableString *
JSStableString::new_(JSContext *cx, const jschar *chars, size_t length)
{
JS_ASSERT(chars[length] == jschar(0));
if (!validateLength(cx, length))
return NULL;
JSFixedString *str = (JSFixedString *)js_NewGCString(cx);
JSStableString *str = (JSStableString *)js_NewGCString(cx);
if (!str)
return NULL;
str->init(chars, length);
return str;
}
JS_ALWAYS_INLINE JSAtom *
JSFixedString::morphAtomizedStringIntoAtom()
{
d.lengthAndFlags = buildLengthAndFlags(length(), ATOM_BIT);
return &asAtom();
}
JS_ALWAYS_INLINE JSInlineString *
JSInlineString::new_(JSContext *cx)
{

View File

@ -22,7 +22,7 @@ bool
JSString::isShort() const
{
bool is_short = (getAllocKind() == gc::FINALIZE_SHORT_STRING);
JS_ASSERT_IF(is_short, isFixed());
JS_ASSERT_IF(is_short, isFlat());
return is_short;
}
#endif
@ -31,7 +31,7 @@ bool
JSString::isExternal() const
{
bool is_external = (getAllocKind() == gc::FINALIZE_EXTERNAL_STRING);
JS_ASSERT_IF(is_external, isFixed());
JS_ASSERT_IF(is_external, isFlat());
return is_external;
}
@ -56,21 +56,19 @@ JSString::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf)
return mallocSizeOf(extensible.chars());
}
JS_ASSERT(isFixed());
// JSExternalString: don't count, the chars could be stored anywhere.
if (isExternal())
return 0;
// JSInlineString, JSShortString, JSInlineAtom, JSShortAtom: the chars are inline.
// JSInlineString, JSShortString [JSInlineAtom, JSShortAtom]: the chars are inline.
if (isInline())
return 0;
// JSAtom, JSFixedString, JSUndependedString: measure the space for the
// JSAtom, JSStableString, JSUndependedString: measure the space for the
// chars. For JSUndependedString, there is no need to count the base
// string, for the same reason as JSDependentString above.
JSFixedString &fixed = asFixed();
return mallocSizeOf(fixed.chars());
JSFlatString &flat = asFlat();
return mallocSizeOf(flat.chars());
}
#ifdef DEBUG
@ -335,7 +333,7 @@ js_ConcatStrings(JSContext *cx, HandleString left, HandleString right)
return JSRope::new_(cx, left, right, wholeLength);
}
JSFixedString *
JSFlatString *
JSDependentString::undepend(JSContext *cx)
{
JS_ASSERT(JSString::isDependent());
@ -363,7 +361,23 @@ JSDependentString::undepend(JSContext *cx)
*/
d.lengthAndFlags = buildLengthAndFlags(n, UNDEPENDED_FLAGS);
return &this->asFixed();
return &this->asFlat();
}
JSStableString *
JSInlineString::uninline(JSContext *maybecx)
{
JS_ASSERT(isInline());
size_t n = length();
size_t nbytes = (n + 1) * sizeof(jschar);
jschar *news = maybecx ? maybecx->pod_malloc<jschar>(n + 1) : js_pod_malloc<jschar>(n + 1);
if (!news)
return NULL;
js_strncpy(news, d.inlineStorage, n);
news[n] = 0;
d.u1.chars = news;
JS_ASSERT(!isInline());
return &asStable();
}
bool
@ -456,7 +470,7 @@ StaticStrings::init(JSContext *cx)
for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
jschar buffer[] = { jschar(i), '\0' };
JSFixedString *s = js_NewStringCopyN(cx, buffer, 1);
JSFlatString *s = js_NewStringCopyN(cx, buffer, 1);
if (!s)
return false;
unitStaticTable[i] = s->morphAtomizedStringIntoAtom();
@ -464,7 +478,7 @@ StaticStrings::init(JSContext *cx)
for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
jschar buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' };
JSFixedString *s = js_NewStringCopyN(cx, buffer, 2);
JSFlatString *s = js_NewStringCopyN(cx, buffer, 2);
if (!s)
return false;
length2StaticTable[i] = s->morphAtomizedStringIntoAtom();
@ -482,7 +496,7 @@ StaticStrings::init(JSContext *cx)
jschar('0' + ((i / 10) % 10)),
jschar('0' + (i % 10)),
'\0' };
JSFixedString *s = js_NewStringCopyN(cx, buffer, 3);
JSFlatString *s = js_NewStringCopyN(cx, buffer, 3);
if (!s)
return false;
intStaticTable[i] = s->morphAtomizedStringIntoAtom();

View File

@ -24,7 +24,8 @@ class JSUndependedString;
class JSExtensibleString;
class JSExternalString;
class JSLinearString;
class JSFixedString;
class JSStableString;
class JSInlineString;
class JSRope;
class JSAtom;
@ -84,43 +85,45 @@ static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
* arrange them into a hierarchy of operations/invariants and represent this
* hierarchy in C++ with classes:
*
* C++ type operations+fields / invariants+properties
*
* JSString (abstract) getCharsZ, getChars, length / -
* C++ type operations+fields / invariants+properties
* ========================== =========================================
* JSString (abstract) getCharsZ, getChars, length / -
* | \
* | JSRope leftChild, rightChild / -
* | JSRope leftChild, rightChild / -
* |
* JSLinearString (abstract) chars / might be null-terminated
* JSLinearString (abstract) chars / might be null-terminated
* | \
* | JSDependentString base / -
* | JSDependentString base / -
* |
* JSFlatString (abstract) - / null-terminated
* | \
* | JSExtensibleString capacity / no external pointers into char array
* JSFlatString - / null terminated
* | |
* | +-- JSStableString - / may have external pointers into char array
* | |
* | +-- JSExternalString - / char array memory managed by embedding
* | |
* | +-- JSExtensibleString capacity / no external pointers into char array
* | |
* | +-- JSUndependedString original dependent base / -
* | |
* | +-- JSInlineString - / chars stored in header
* | \
* | JSShortString - / header is fat
* |
* JSFixedString - / may have external pointers into char array
* | \ \ \
* | \ \ JSUndependedString original dependent base / -
* | \ \
* | \ JSExternalString - / char array memory managed by embedding
* | \
* | JSInlineString - / chars stored in header
* | | \
* | | JSShortString - / header is fat
* | | |
* JSAtom | | - / string equality === pointer equality
* | \ | |
* | JSInlineAtom | - / atomized JSInlineString
* | \ |
* | JSShortAtom - / atomized JSShortString
* JSAtom - / string equality === pointer equality
* |
* js::PropertyName - / chars don't contain an index (uint32_t)
* js::PropertyName - / chars don't contain an index (uint32_t)
*
* Classes marked with (abstract) above are not literally C++ Abstract Base
* Classes (since there are no virtual functions, pure or not, in this
* hierarchy), but have the same meaning: there are no strings with this type as
* its most-derived type.
*
* Technically, there are three additional most-derived types that satisfy the
* invariants of more than one of the abovementioned most-derived types:
* - InlineAtom = JSInlineString + JSAtom (atom with inline chars)
* - ShortAtom = JSShortString + JSAtom (atom with (more) inline chars)
* - StableAtom = JSStableString + JSAtom (atom with out-of-line chars)
*
* Derived string types can be queried from ancestor types via isX() and
* retrieved with asX() debug-only-checked casts.
*
@ -185,8 +188,8 @@ class JSString : public js::gc::Cell
* Flat - isLinear && !isDependent
* Undepended 0011 0011
* Extensible 0010 0010
* Fixed 0100 isFlat && !isExtensible
* Inline 0100 isFixed && (u1.chars == inlineStorage) || isInt32)
* Inline 0100 isFlat && !isExtensible && (u1.chars == inlineStorage) || isInt32)
* Stable 0100 isFlat && !isExtensible && (u1.chars != inlineStorage)
* Short 0100 header in FINALIZE_SHORT_STRING arena
* External 0100 header in FINALIZE_EXTERNAL_STRING arena
* Int32 0110 x110 (NYI, Bug 654190)
@ -271,7 +274,7 @@ class JSString : public js::gc::Cell
inline JSLinearString *ensureLinear(JSContext *cx);
inline JSFlatString *ensureFlat(JSContext *cx);
inline JSFixedString *ensureFixed(JSContext *cx);
inline JSStableString *ensureStable(JSContext *cx);
static bool ensureLinear(JSContext *cx, JSString *str) {
return str->ensureLinear(cx) != NULL;
@ -334,20 +337,21 @@ class JSString : public js::gc::Cell
return *(JSExtensibleString *)this;
}
JS_ALWAYS_INLINE
bool isFixed() const {
return isFlat() && !isExtensible();
}
JS_ALWAYS_INLINE
JSFixedString &asFixed() const {
JS_ASSERT(isFixed());
return *(JSFixedString *)this;
}
JS_ALWAYS_INLINE
bool isInline() const {
return isFixed() && (d.u1.chars == d.inlineStorage);
return isFlat() && !isExtensible() && (d.u1.chars == d.inlineStorage);
}
JS_ALWAYS_INLINE
JSInlineString &asInline() const {
JS_ASSERT(isInline());
return *(JSInlineString *)this;
}
JS_ALWAYS_INLINE
JSStableString &asStable() const {
JS_ASSERT(!isInline());
return *(JSStableString *)this;
}
/* For hot code, prefer other type queries. */
@ -476,7 +480,7 @@ JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString));
class JSDependentString : public JSLinearString
{
friend class JSString;
JSFixedString *undepend(JSContext *cx);
JSFlatString *undepend(JSContext *cx);
void init(JSLinearString *base, const jschar *chars, size_t length);
@ -525,11 +529,27 @@ class JSFlatString : public JSLinearString
*/
inline js::PropertyName *toPropertyName(JSContext *cx);
/*
* Once a JSFlatString sub-class has been added to the atom state, this
* operation changes the string to the JSAtom type, in place.
*/
inline JSAtom *morphAtomizedStringIntoAtom();
inline void finalize(js::FreeOp *fop);
};
JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
class JSStableString : public JSFlatString
{
void init(const jschar *chars, size_t length);
public:
static inline JSStableString *new_(JSContext *cx, const jschar *chars, size_t length);
};
JS_STATIC_ASSERT(sizeof(JSStableString) == sizeof(JSString));
class JSExtensibleString : public JSFlatString
{
/* Vacuous and therefore unimplemented. */
@ -546,29 +566,7 @@ class JSExtensibleString : public JSFlatString
JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
class JSFixedString : public JSFlatString
{
void init(const jschar *chars, size_t length);
/* Vacuous and therefore unimplemented. */
JSFlatString *ensureFixed(JSContext *cx) MOZ_DELETE;
bool isFixed() const MOZ_DELETE;
JSFixedString &asFixed() const MOZ_DELETE;
public:
static inline JSFixedString *new_(JSContext *cx, const jschar *chars, size_t length);
/*
* Once a JSFixedString has been added to the atom state, this operation
* changes the type (in place, as reflected by the flag bits) of the
* JSFixedString into a JSAtom.
*/
inline JSAtom *morphAtomizedStringIntoAtom();
};
JS_STATIC_ASSERT(sizeof(JSFixedString) == sizeof(JSString));
class JSInlineString : public JSFixedString
class JSInlineString : public JSFlatString
{
static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
@ -577,12 +575,13 @@ class JSInlineString : public JSFixedString
inline jschar *init(size_t length);
JSStableString *uninline(JSContext *cx);
inline void resetLength(size_t length);
static bool lengthFits(size_t length) {
return length <= MAX_INLINE_LENGTH;
}
};
JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
@ -620,7 +619,7 @@ class JSShortString : public JSInlineString
JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
class JSExternalString : public JSFixedString
class JSExternalString : public JSFlatString
{
void init(const jschar *chars, size_t length, const JSStringFinalizer *fin);
@ -644,7 +643,7 @@ class JSExternalString : public JSFixedString
JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
class JSUndependedString : public JSFixedString
class JSUndependedString : public JSFlatString
{
/*
* JSUndependedString is not explicitly used and is only present for
@ -655,7 +654,7 @@ class JSUndependedString : public JSFixedString
JS_STATIC_ASSERT(sizeof(JSUndependedString) == sizeof(JSString));
class JSAtom : public JSFixedString
class JSAtom : public JSFlatString
{
/* Vacuous and therefore unimplemented. */
bool isAtom() const MOZ_DELETE;
@ -674,26 +673,6 @@ class JSAtom : public JSFixedString
JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
class JSInlineAtom : public JSInlineString /*, JSAtom */
{
/*
* JSInlineAtom is not explicitly used and is only present for consistency.
* See Atomize() for how JSInlineStrings get morphed into JSInlineAtoms.
*/
};
JS_STATIC_ASSERT(sizeof(JSInlineAtom) == sizeof(JSInlineString));
class JSShortAtom : public JSShortString /*, JSInlineAtom */
{
/*
* JSShortAtom is not explicitly used and is only present for consistency.
* See Atomize() for how JSShortStrings get morphed into JSShortAtoms.
*/
};
JS_STATIC_ASSERT(sizeof(JSShortAtom) == sizeof(JSShortString));
namespace js {
class StaticStrings
@ -835,14 +814,29 @@ JSString::ensureFlat(JSContext *cx)
: asRope().flatten(cx);
}
JS_ALWAYS_INLINE JSFixedString *
JSString::ensureFixed(JSContext *cx)
JS_ALWAYS_INLINE JSStableString *
JSString::ensureStable(JSContext *maybecx)
{
if (!ensureFlat(cx))
return NULL;
if (isExtensible())
d.lengthAndFlags = buildLengthAndFlags(length(), FIXED_FLAGS);
return &asFixed();
if (isRope()) {
JSFlatString *flat = asRope().flatten(maybecx);
if (!flat)
return NULL;
JS_ASSERT(!flat->isInline());
return &flat->asStable();
}
if (isDependent()) {
JSFlatString *flat = asDependent().undepend(maybecx);
if (!flat)
return NULL;
return &flat->asStable();
}
if (!isInline())
return &asStable();
JS_ASSERT(isInline());
return asInline().uninline(maybecx);
}
inline JSLinearString *

View File

@ -38,7 +38,7 @@ StringBuffer::extractWellSized()
return buf;
}
JSFixedString *
JSFlatString *
StringBuffer::finishString()
{
JSContext *cx = context();
@ -60,7 +60,7 @@ StringBuffer::finishString()
if (!buf)
return NULL;
JSFixedString *str = js_NewString(cx, buf, length);
JSFlatString *str = js_NewString(cx, buf, length);
if (!str)
js_free(buf);
return str;

View File

@ -82,7 +82,7 @@ class StringBuffer
* Creates a string from the characters in this buffer, then (regardless
* whether string creation succeeded or failed) empties the buffer.
*/
JSFixedString *finishString();
JSFlatString *finishString();
/* Identical to finishString() except that an atom is created. */
JSAtom *finishAtom();