mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
9263053a92
commit
e435199147
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
@ -79,7 +79,6 @@ class JSDependentString;
|
||||
class JSExtensibleString;
|
||||
class JSExternalString;
|
||||
class JSLinearString;
|
||||
class JSFixedString;
|
||||
class JSRope;
|
||||
class JSAtom;
|
||||
class JSWrapper;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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 *
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user