Bug 1128528 - Don't unnecessarily require flat strings, to avoid wasting a ton of memory in pathological cases. r=luke

This commit is contained in:
Jan de Mooij 2015-02-04 11:16:02 +01:00
parent 38649f4a52
commit 810f7413a5
5 changed files with 62 additions and 62 deletions

View File

@ -191,7 +191,7 @@ ParseEvalStringAsJSON(JSContext *cx, const mozilla::Range<const CharT> chars, Mu
} }
static EvalJSONResult static EvalJSONResult
TryEvalJSON(JSContext *cx, JSFlatString *str, MutableHandleValue rval) TryEvalJSON(JSContext *cx, JSLinearString *str, MutableHandleValue rval)
{ {
if (str->hasLatin1Chars()) { if (str->hasLatin1Chars()) {
AutoCheckCannotGC nogc; AutoCheckCannotGC nogc;
@ -203,13 +203,13 @@ TryEvalJSON(JSContext *cx, JSFlatString *str, MutableHandleValue rval)
return EvalJSON_NotJSON; return EvalJSON_NotJSON;
} }
AutoStableStringChars flatChars(cx); AutoStableStringChars linearChars(cx);
if (!flatChars.init(cx, str)) if (!linearChars.init(cx, str))
return EvalJSON_Failure; return EvalJSON_Failure;
return flatChars.isLatin1() return linearChars.isLatin1()
? ParseEvalStringAsJSON(cx, flatChars.latin1Range(), rval) ? ParseEvalStringAsJSON(cx, linearChars.latin1Range(), rval)
: ParseEvalStringAsJSON(cx, flatChars.twoByteRange(), rval); : ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
} }
// Define subset of ExecuteType so that casting performs the injection. // Define subset of ExecuteType so that casting performs the injection.
@ -277,19 +277,19 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
thisv = ObjectValue(*thisobj); thisv = ObjectValue(*thisobj);
} }
Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); RootedLinearString linearStr(cx, str->ensureLinear(cx));
if (!flatStr) if (!linearStr)
return false; return false;
RootedScript callerScript(cx, caller ? caller.script() : nullptr); RootedScript callerScript(cx, caller ? caller.script() : nullptr);
EvalJSONResult ejr = TryEvalJSON(cx, flatStr, args.rval()); EvalJSONResult ejr = TryEvalJSON(cx, linearStr, args.rval());
if (ejr != EvalJSON_NotJSON) if (ejr != EvalJSON_NotJSON)
return ejr == EvalJSON_Success; return ejr == EvalJSON_Success;
EvalScriptGuard esg(cx); EvalScriptGuard esg(cx);
if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame()) if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
esg.lookupInEvalCache(flatStr, callerScript, pc); esg.lookupInEvalCache(linearStr, callerScript, pc);
if (!esg.foundScript()) { if (!esg.foundScript()) {
RootedScript maybeScript(cx); RootedScript maybeScript(cx);
@ -323,18 +323,18 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset) .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
.maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc)); .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc));
AutoStableStringChars flatChars(cx); AutoStableStringChars linearChars(cx);
if (!flatChars.initTwoByte(cx, flatStr)) if (!linearChars.initTwoByte(cx, linearStr))
return false; return false;
const char16_t *chars = flatChars.twoByteRange().start().get(); const char16_t *chars = linearChars.twoByteRange().start().get();
SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller() SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership ? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership; : SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, flatStr->length(), ownership); SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
scopeobj, callerScript, staticScope, scopeobj, callerScript, staticScope,
options, srcBuf, flatStr, staticLevel); options, srcBuf, linearStr, staticLevel);
if (!compiled) if (!compiled)
return false; return false;
@ -366,17 +366,17 @@ js::DirectEvalStringFromIon(JSContext *cx,
unsigned staticLevel = callerScript->staticLevel() + 1; unsigned staticLevel = callerScript->staticLevel() + 1;
Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); RootedLinearString linearStr(cx, str->ensureLinear(cx));
if (!flatStr) if (!linearStr)
return false; return false;
EvalJSONResult ejr = TryEvalJSON(cx, flatStr, vp); EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp);
if (ejr != EvalJSON_NotJSON) if (ejr != EvalJSON_NotJSON)
return ejr == EvalJSON_Success; return ejr == EvalJSON_Success;
EvalScriptGuard esg(cx); EvalScriptGuard esg(cx);
esg.lookupInEvalCache(flatStr, callerScript, pc); esg.lookupInEvalCache(linearStr, callerScript, pc);
if (!esg.foundScript()) { if (!esg.foundScript()) {
RootedScript maybeScript(cx); RootedScript maybeScript(cx);
@ -405,18 +405,18 @@ js::DirectEvalStringFromIon(JSContext *cx,
.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset) .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
.maybeMakeStrictMode(IsStrictEvalPC(pc)); .maybeMakeStrictMode(IsStrictEvalPC(pc));
AutoStableStringChars flatChars(cx); AutoStableStringChars linearChars(cx);
if (!flatChars.initTwoByte(cx, flatStr)) if (!linearChars.initTwoByte(cx, linearStr))
return false; return false;
const char16_t *chars = flatChars.twoByteRange().start().get(); const char16_t *chars = linearChars.twoByteRange().start().get();
SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller() SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership ? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership; : SourceBufferHolder::NoOwnership;
SourceBufferHolder srcBuf(chars, flatStr->length(), ownership); SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
scopeobj, callerScript, staticScope, scopeobj, callerScript, staticScope,
options, srcBuf, flatStr, staticLevel); options, srcBuf, linearStr, staticLevel);
if (!compiled) if (!compiled)
return false; return false;

View File

@ -836,20 +836,20 @@ json_parse(JSContext *cx, unsigned argc, Value *vp)
if (!str) if (!str)
return false; return false;
JSFlatString *flat = str->ensureFlat(cx); JSLinearString *linear = str->ensureLinear(cx);
if (!flat) if (!linear)
return false; return false;
AutoStableStringChars flatChars(cx); AutoStableStringChars linearChars(cx);
if (!flatChars.init(cx, flat)) if (!linearChars.init(cx, linear))
return false; return false;
RootedValue reviver(cx, args.get(1)); HandleValue reviver = args.get(1);
/* Steps 2-5. */ /* Steps 2-5. */
return flatChars.isLatin1() return linearChars.isLatin1()
? ParseJSONWithReviver(cx, flatChars.latin1Range(), reviver, args.rval()) ? ParseJSONWithReviver(cx, linearChars.latin1Range(), reviver, args.rval())
: ParseJSONWithReviver(cx, flatChars.twoByteRange(), reviver, args.rval()); : ParseJSONWithReviver(cx, linearChars.twoByteRange(), reviver, args.rval());
} }
/* ES5 15.12.3. */ /* ES5 15.12.3. */

View File

@ -3537,18 +3537,18 @@ reflect_parse(JSContext *cx, uint32_t argc, jsval *vp)
if (!serialize.init(builder)) if (!serialize.init(builder))
return false; return false;
JSFlatString *flat = src->ensureFlat(cx); JSLinearString *linear = src->ensureLinear(cx);
if (!flat) if (!linear)
return false; return false;
AutoStableStringChars flatChars(cx); AutoStableStringChars linearChars(cx);
if (!flatChars.initTwoByte(cx, flat)) if (!linearChars.initTwoByte(cx, linear))
return false; return false;
CompileOptions options(cx); CompileOptions options(cx);
options.setFileAndLine(filename, lineno); options.setFileAndLine(filename, lineno);
options.setCanLazilyParse(false); options.setCanLazilyParse(false);
mozilla::Range<const char16_t> chars = flatChars.twoByteRange(); mozilla::Range<const char16_t> chars = linearChars.twoByteRange();
Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars.start().get(), Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars.start().get(),
chars.length(), /* foldConstants = */ false, nullptr, nullptr); chars.length(), /* foldConstants = */ false, nullptr, nullptr);
if (!parser.checkOptions()) if (!parser.checkOptions())

View File

@ -3065,32 +3065,32 @@ CopySubstringsToFatInline(JSFatInlineString *dest, const CharT *src, const Strin
} }
static inline JSFatInlineString * static inline JSFatInlineString *
FlattenSubstrings(JSContext *cx, Handle<JSFlatString*> flatStr, const StringRange *ranges, FlattenSubstrings(JSContext *cx, HandleLinearString str, const StringRange *ranges,
size_t rangesLen, size_t outputLen) size_t rangesLen, size_t outputLen)
{ {
JSFatInlineString *str = NewGCFatInlineString<CanGC>(cx); JSFatInlineString *result = NewGCFatInlineString<CanGC>(cx);
if (!str) if (!result)
return nullptr; return nullptr;
AutoCheckCannotGC nogc; AutoCheckCannotGC nogc;
if (flatStr->hasLatin1Chars()) if (str->hasLatin1Chars())
CopySubstringsToFatInline(str, flatStr->latin1Chars(nogc), ranges, rangesLen, outputLen); CopySubstringsToFatInline(result, str->latin1Chars(nogc), ranges, rangesLen, outputLen);
else else
CopySubstringsToFatInline(str, flatStr->twoByteChars(nogc), ranges, rangesLen, outputLen); CopySubstringsToFatInline(result, str->twoByteChars(nogc), ranges, rangesLen, outputLen);
return str; return result;
} }
static JSString * static JSString *
AppendSubstrings(JSContext *cx, Handle<JSFlatString*> flatStr, AppendSubstrings(JSContext *cx, HandleLinearString str, const StringRange *ranges,
const StringRange *ranges, size_t rangesLen) size_t rangesLen)
{ {
MOZ_ASSERT(rangesLen); MOZ_ASSERT(rangesLen);
/* For single substrings, construct a dependent string. */ /* For single substrings, construct a dependent string. */
if (rangesLen == 1) if (rangesLen == 1)
return NewDependentString(cx, flatStr, ranges[0].start, ranges[0].length); return NewDependentString(cx, str, ranges[0].start, ranges[0].length);
bool isLatin1 = flatStr->hasLatin1Chars(); bool isLatin1 = str->hasLatin1Chars();
uint32_t fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; uint32_t fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
if (isLatin1) if (isLatin1)
fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_LATIN1; fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_LATIN1;
@ -3113,10 +3113,10 @@ AppendSubstrings(JSContext *cx, Handle<JSFlatString*> flatStr,
if (i == end) { if (i == end) {
/* Not even one range fits JSFatInlineString, use DependentString */ /* Not even one range fits JSFatInlineString, use DependentString */
const StringRange &sr = ranges[i++]; const StringRange &sr = ranges[i++];
part = NewDependentString(cx, flatStr, sr.start, sr.length); part = NewDependentString(cx, str, sr.start, sr.length);
} else { } else {
/* Copy the ranges (linearly) into a JSFatInlineString */ /* Copy the ranges (linearly) into a JSFatInlineString */
part = FlattenSubstrings(cx, flatStr, ranges + i, end - i, substrLen); part = FlattenSubstrings(cx, str, ranges + i, end - i, substrLen);
i = end; i = end;
} }
@ -3134,13 +3134,13 @@ AppendSubstrings(JSContext *cx, Handle<JSFlatString*> flatStr,
static bool static bool
StrReplaceRegexpRemove(JSContext *cx, HandleString str, RegExpShared &re, MutableHandleValue rval) StrReplaceRegexpRemove(JSContext *cx, HandleString str, RegExpShared &re, MutableHandleValue rval)
{ {
Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); RootedLinearString linearStr(cx, str->ensureLinear(cx));
if (!flatStr) if (!linearStr)
return false; return false;
Vector<StringRange, 16, SystemAllocPolicy> ranges; Vector<StringRange, 16, SystemAllocPolicy> ranges;
size_t charsLen = flatStr->length(); size_t charsLen = linearStr->length();
ScopedMatchPairs matches(&cx->tempLifoAlloc()); ScopedMatchPairs matches(&cx->tempLifoAlloc());
size_t startIndex = 0; /* Index used for iterating through the string. */ size_t startIndex = 0; /* Index used for iterating through the string. */
@ -3152,7 +3152,7 @@ StrReplaceRegexpRemove(JSContext *cx, HandleString str, RegExpShared &re, Mutabl
if (!CheckForInterrupt(cx)) if (!CheckForInterrupt(cx))
return false; return false;
RegExpRunStatus status = re.execute(cx, flatStr, startIndex, &matches); RegExpRunStatus status = re.execute(cx, linearStr, startIndex, &matches);
if (status == RegExpRunStatus_Error) if (status == RegExpRunStatus_Error)
return false; return false;
if (status == RegExpRunStatus_Success_NotFound) if (status == RegExpRunStatus_Success_NotFound)
@ -3183,7 +3183,7 @@ StrReplaceRegexpRemove(JSContext *cx, HandleString str, RegExpShared &re, Mutabl
res = cx->global()->getRegExpStatics(cx); res = cx->global()->getRegExpStatics(cx);
if (!res) if (!res)
return false; return false;
res->updateLazily(cx, flatStr, &re, lazyIndex); res->updateLazily(cx, linearStr, &re, lazyIndex);
} }
rval.setString(str); rval.setString(str);
return true; return true;
@ -3194,7 +3194,7 @@ StrReplaceRegexpRemove(JSContext *cx, HandleString str, RegExpShared &re, Mutabl
if (!res) if (!res)
return false; return false;
res->updateLazily(cx, flatStr, &re, lazyIndex); res->updateLazily(cx, linearStr, &re, lazyIndex);
/* Include any remaining part of the string. */ /* Include any remaining part of the string. */
if (lastIndex < charsLen) { if (lastIndex < charsLen) {
@ -3208,7 +3208,7 @@ StrReplaceRegexpRemove(JSContext *cx, HandleString str, RegExpShared &re, Mutabl
return true; return true;
} }
JSString *result = AppendSubstrings(cx, flatStr, ranges.begin(), ranges.length()); JSString *result = AppendSubstrings(cx, linearStr, ranges.begin(), ranges.length());
if (!result) if (!result)
return false; return false;

View File

@ -6016,8 +6016,8 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName, const Value &code
fullMethodName, "string", InformalValueTypeName(code)); fullMethodName, "string", InformalValueTypeName(code));
return false; return false;
} }
Rooted<JSFlatString *> flat(cx, code.toString()->ensureFlat(cx)); RootedLinearString linear(cx, code.toString()->ensureLinear(cx));
if (!flat) if (!linear)
return false; return false;
/* /*
@ -6128,7 +6128,7 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName, const Value &code
AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr(); AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
jsbytecode *pc = iter ? iter->pc() : nullptr; jsbytecode *pc = iter ? iter->pc() : nullptr;
AutoStableStringChars stableChars(cx); AutoStableStringChars stableChars(cx);
if (!stableChars.initTwoByte(cx, flat)) if (!stableChars.initTwoByte(cx, linear))
return false; return false;
mozilla::Range<const char16_t> chars = stableChars.twoByteRange(); mozilla::Range<const char16_t> chars = stableChars.twoByteRange();