Bug 637905 - Add (asserted-infallible) append methods to js::Vector, for use when the vector in question has previously had space reserved, and use them a bunch of places. r=luke

--HG--
extra : rebase_source : 89002f1cb5160d89e90a22e54bdca1d57bf9deda
This commit is contained in:
Jeff Walden 2011-02-18 18:32:29 -08:00
parent 6aa57eef52
commit 6a6126ed94
13 changed files with 150 additions and 58 deletions

View File

@ -4782,8 +4782,8 @@ NewFunctionInfo(JSContext* cx,
if (!ffiType)
return NULL;
fninfo->mArgTypes.append(argType);
fninfo->mFFITypes.append(ffiType);
fninfo->mArgTypes.infallibleAppend(argType);
fninfo->mFFITypes.infallibleAppend(ffiType);
}
if (fninfo->mIsVariadic)

View File

@ -1336,13 +1336,14 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
Value targetv = ObjectValue(*obj);
WrapperVector &vector = cx->runtime->compartments;
AutoValueVector toTransplant(cx);
toTransplant.reserve(vector.length());
if (!toTransplant.reserve(vector.length()))
return NULL;
for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) {
WrapperMap &pmap = (*p)->crossCompartmentWrappers;
if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
// We found a wrapper. Remember and root it.
toTransplant.append(wp->value);
toTransplant.infallibleAppend(wp->value);
}
}
@ -1429,13 +1430,14 @@ js_TransplantObjectWithWrapper(JSContext *cx,
Value targetv = ObjectValue(*targetobj);
WrapperVector &vector = cx->runtime->compartments;
AutoValueVector toTransplant(cx);
toTransplant.reserve(vector.length());
if (!toTransplant.reserve(vector.length()))
return NULL;
for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) {
WrapperMap &pmap = (*p)->crossCompartmentWrappers;
if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
// We found a wrapper. Remember and root it.
toTransplant.append(wp->value);
toTransplant.infallibleAppend(wp->value);
}
}

View File

@ -3257,7 +3257,7 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
*/
jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length);
js::AutoValueVector vector(cx);
AutoValueVector vector(cx);
if (!vector.reserve(jsvalCount))
return JS_FALSE;
@ -3277,7 +3277,7 @@ js_CloneDensePrimitiveArray(JSContext *cx, JSObject *obj, JSObject **clone)
return JS_TRUE;
}
vector.append(val);
vector.infallibleAppend(val);
}
*clone = NewDenseCopiedArray(cx, jsvalCount, vector.begin());

View File

@ -3242,6 +3242,9 @@ class AutoVectorRooter : protected AutoGCRooter
bool append(const T &v) { return vector.append(v); }
/* For use when space has already been reserved. */
void infallibleAppend(const T &v) { vector.infallibleAppend(v); }
void popBack() { vector.popBack(); }
bool growBy(size_t inc) {

View File

@ -1887,7 +1887,7 @@ obj_keys(JSContext *cx, uintN argc, Value *vp)
JSString *str = js_IntToString(cx, JSID_TO_INT(id));
if (!str)
return false;
JS_ALWAYS_TRUE(vals.append(StringValue(str)));
vals.infallibleAppend(StringValue(str));
} else {
JS_ASSERT(JSID_IS_OBJECT(id));
}

View File

@ -1841,7 +1841,7 @@ ASTSerializer::statements(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!sourceElement(next, &elt))
return false;
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
elts.infallibleAppend(elt);
}
return true;
@ -1857,7 +1857,7 @@ ASTSerializer::expressions(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!expression(next, &elt))
return false;
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
elts.infallibleAppend(elt);
}
return true;
@ -1873,7 +1873,7 @@ ASTSerializer::xmls(JSParseNode *pn, NodeVector &elts)
Value elt;
if (!xml(next, &elt))
return false;
JS_ALWAYS_TRUE(elts.append(elt)); /* space check above */
elts.infallibleAppend(elt);
}
return true;
@ -1938,8 +1938,6 @@ ASTSerializer::variableDeclaration(JSParseNode *pn, bool let, Value *dst)
VarDeclKind kind = let ? VARDECL_LET : VARDECL_VAR;
NodeVector dtors(cx);
if (!dtors.reserve(pn->pn_count))
return false;
/* In a for-in context, variable declarations contain just a single pattern. */
if (pn->pn_xflags & PNX_FORINVAR) {
@ -1950,11 +1948,13 @@ ASTSerializer::variableDeclaration(JSParseNode *pn, bool let, Value *dst)
builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst);
}
if (!dtors.reserve(pn->pn_count))
return false;
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
Value child;
if (!variableDeclarator(next, &kind, &child))
return false;
JS_ALWAYS_TRUE(dtors.append(child)); /* space check above */
dtors.infallibleAppend(child);
}
return builder.variableDeclaration(dtors, kind, &pn->pn_pos, dst);
@ -2000,7 +2000,7 @@ ASTSerializer::letHead(JSParseNode *pn, NodeVector &dtors)
*/
if (!variableDeclarator(next, &kind, &child))
return false;
JS_ALWAYS_TRUE(dtors.append(child)); /* space check above */
dtors.infallibleAppend(child);
}
return true;
@ -2048,7 +2048,7 @@ ASTSerializer::switchStatement(JSParseNode *pn, Value *dst)
#endif
if (!switchCase(next, &child))
return false;
JS_ALWAYS_TRUE(cases.append(child)); /* space check above */
cases.infallibleAppend(child);
}
return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst);
@ -2081,7 +2081,7 @@ ASTSerializer::tryStatement(JSParseNode *pn, Value *dst)
Value clause;
if (!catchClause(next->pn_expr, &clause))
return false;
JS_ALWAYS_TRUE(clauses.append(clause)); /* space check above */
clauses.infallibleAppend(clause);
}
}
@ -2578,7 +2578,7 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
Value arg;
if (!expression(next, &arg))
return false;
JS_ALWAYS_TRUE(args.append(arg)); /* space check above */
args.infallibleAppend(arg);
}
return PN_TYPE(pn) == TOK_NEW
@ -2616,12 +2616,12 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
if (PN_TYPE(next) == TOK_COMMA) {
JS_ALWAYS_TRUE(elts.append(MagicValue(JS_SERIALIZE_NO_NODE))); /* space check above */
elts.infallibleAppend(MagicValue(JS_SERIALIZE_NO_NODE));
} else {
Value expr;
if (!expression(next, &expr))
return false;
JS_ALWAYS_TRUE(elts.append(expr)); /* space check above */
elts.infallibleAppend(expr);
}
}
@ -2638,7 +2638,7 @@ ASTSerializer::expression(JSParseNode *pn, Value *dst)
Value prop;
if (!property(next, &prop))
return false;
JS_ALWAYS_TRUE(elts.append(prop)); /* space check above */
elts.infallibleAppend(prop);
}
return builder.objectExpression(elts, &pn->pn_pos, dst);
@ -2937,12 +2937,12 @@ ASTSerializer::arrayPattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
for (JSParseNode *next = pn->pn_head; next; next = next->pn_next) {
if (PN_TYPE(next) == TOK_COMMA) {
JS_ALWAYS_TRUE(elts.append(MagicValue(JS_SERIALIZE_NO_NODE))); /* space check above */
elts.infallibleAppend(MagicValue(JS_SERIALIZE_NO_NODE));
} else {
Value patt;
if (!pattern(next, pkind, &patt))
return false;
JS_ALWAYS_TRUE(elts.append(patt)); /* space check above */
elts.infallibleAppend(patt);
}
}
@ -2968,7 +2968,7 @@ ASTSerializer::objectPattern(JSParseNode *pn, VarDeclKind *pkind, Value *dst)
return false;
}
JS_ALWAYS_TRUE(elts.append(prop)); /* space check above */
elts.infallibleAppend(prop);
}
return builder.objectPattern(elts, &pn->pn_pos, dst);

View File

@ -588,9 +588,9 @@ js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp)
if (size_t len = src->length()) {
if (!sb.reserve(len + 2))
return false;
JS_ALWAYS_TRUE(sb.append('/'));
JS_ALWAYS_TRUE(sb.append(src->chars(), len));
JS_ALWAYS_TRUE(sb.append('/'));
sb.infallibleAppend('/');
sb.infallibleAppend(src->chars(), len);
sb.infallibleAppend('/');
} else {
if (!sb.append("/(?:)/"))
return false;
@ -641,7 +641,7 @@ EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
if (!newChars.length()) {
if (!newChars.reserve(oldLen + 1))
return NULL;
JS_ALWAYS_TRUE(newChars.append(oldChars, size_t(it - oldChars)));
newChars.infallibleAppend(oldChars, size_t(it - oldChars));
}
if (!newChars.append('\\'))
return NULL;
@ -651,17 +651,17 @@ EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
return NULL;
}
if (newChars.length()) {
size_t len = newChars.length();
if (!newChars.append('\0'))
return NULL;
jschar *chars = newChars.extractRawBuffer();
JSString *escaped = js_NewString(cx, chars, len);
if (!escaped)
cx->free(chars);
return escaped;
}
return unescaped;
if (newChars.empty())
return unescaped;
size_t len = newChars.length();
if (!newChars.append('\0'))
return NULL;
jschar *chars = newChars.extractRawBuffer();
JSString *escaped = js_NewString(cx, chars, len);
if (!escaped)
cx->free(chars);
return escaped;
}
static bool

View File

@ -73,7 +73,7 @@ class RegExpStatics
void copyTo(RegExpStatics &dst) {
dst.matchPairs.clear();
/* 'save' has already reserved space in matchPairs */
JS_ALWAYS_TRUE(dst.matchPairs.append(matchPairs));
dst.matchPairs.infallibleAppend(matchPairs);
dst.matchPairsInput = matchPairsInput;
dst.pendingInput = pendingInput;
dst.flags = flags;

View File

@ -521,9 +521,9 @@ RegExp::compile(JSContext *cx)
StringBuffer sb(cx);
if (!sb.reserve(JS_ARRAY_LENGTH(prefix) + source->length() + JS_ARRAY_LENGTH(postfix)))
return false;
JS_ALWAYS_TRUE(sb.append(prefix, JS_ARRAY_LENGTH(prefix)));
JS_ALWAYS_TRUE(sb.append(source->chars(), source->length()));
JS_ALWAYS_TRUE(sb.append(postfix, JS_ARRAY_LENGTH(postfix)));
sb.infallibleAppend(prefix, JS_ARRAY_LENGTH(prefix));
sb.infallibleAppend(source->chars(), source->length());
sb.infallibleAppend(postfix, JS_ARRAY_LENGTH(postfix));
JSLinearString *fakeySource = sb.finishString();
if (!fakeySource)

View File

@ -2146,14 +2146,14 @@ DoReplace(JSContext *cx, RegExpStatics *res, ReplaceData &rdata)
for (; dp; dp = js_strchr_limit(dp, '$', ep)) {
/* Move one of the constant portions of the replacement value. */
size_t len = dp - cp;
JS_ALWAYS_TRUE(rdata.sb.append(cp, len));
rdata.sb.infallibleAppend(cp, len);
cp = dp;
JSSubString sub;
size_t skip;
if (InterpretDollar(cx, res, dp, ep, rdata, &sub, &skip)) {
len = sub.length;
JS_ALWAYS_TRUE(rdata.sb.append(sub.chars, len));
rdata.sb.infallibleAppend(sub.chars, len);
cp += skip;
dp += skip;
} else {
@ -2182,7 +2182,7 @@ ReplaceRegExpCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p)
size_t growth = leftlen + replen;
if (!rdata.sb.reserve(rdata.sb.length() + growth))
return false;
JS_ALWAYS_TRUE(rdata.sb.append(left, leftlen)); /* skipped-over portion of the search value */
rdata.sb.infallibleAppend(left, leftlen); /* skipped-over portion of the search value */
DoReplace(cx, res, rdata);
return true;
}
@ -2294,7 +2294,7 @@ BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *reps
return false;
/* Move the pre-dollar chunk in bulk. */
JS_ALWAYS_TRUE(newReplaceChars.append(repstr->chars(), firstDollar));
newReplaceChars.infallibleAppend(repstr->chars(), firstDollar);
/* Move the rest char-by-char, interpreting dollars as we encounter them. */
#define ENSURE(__cond) if (!(__cond)) return false;

View File

@ -84,6 +84,21 @@ class StringBuffer
bool append(JSAtom *atom);
bool appendN(const jschar c, size_t n);
bool appendInflated(const char *cstr, size_t len);
/* Infallible variants usable when the corresponding space is reserved. */
void infallibleAppend(const jschar c) {
cb.infallibleAppend(c);
}
void infallibleAppend(const jschar *chars, size_t len) {
cb.infallibleAppend(chars, len);
}
void infallibleAppend(const jschar *begin, const jschar *end) {
cb.infallibleAppend(begin, end);
}
void infallibleAppendN(const jschar c, size_t n) {
cb.infallibleAppendN(c, n);
}
JSAtom *atomize(uintN flags = 0);
static JSAtom *atomize(JSContext *cx, const CharBuffer &cb, uintN flags = 0);
static JSAtom *atomize(JSContext *cx, const jschar *begin, size_t length, uintN flags = 0);

View File

@ -225,11 +225,16 @@ class Vector : AllocPolicy
/*
* Pointer to the buffer, be it inline or heap-allocated. Only [mBegin,
* mBegin + mLength) hold valid constructed T objects. The range [mBegin +
* mLength, mBegin + mCapacity) holds uninitialized memory.
* mLength, mBegin + mCapacity) holds uninitialized memory. The range
* [mBegin + mLength, mBegin + mReserved) also holds uninitialized memory
* previously allocated by a call to reserve().
*/
T *mBegin;
size_t mLength; /* Number of elements in the Vector. */
size_t mCapacity; /* Max number of elements storable in the Vector without resizing. */
#ifdef DEBUG
size_t mReserved; /* Max elements of reserved or used space in this vector. */
#endif
AlignedStorage<sInlineBytes> storage;
@ -259,6 +264,14 @@ class Vector : AllocPolicy
return mBegin + mLength;
}
#ifdef DEBUG
size_t reserved() const {
JS_ASSERT(mReserved <= mCapacity);
JS_ASSERT(mLength <= mReserved);
return mReserved;
}
#endif
public:
typedef T ElementType;
@ -325,7 +338,10 @@ class Vector : AllocPolicy
/* If reserve(length() + N) succeeds, the N next appends are guaranteed to succeed. */
bool reserve(size_t capacity);
/* Destroy elements in the range [begin() + incr, end()). */
/*
* Destroy elements in the range [end() - incr, end()). Does not deallocate
* or unreserve storage for those elements.
*/
void shrinkBy(size_t incr);
/* Grow the vector by incr elements. */
@ -338,14 +354,41 @@ class Vector : AllocPolicy
bool growByUninitialized(size_t incr);
bool resizeUninitialized(size_t newLength);
/* Shorthand for shrinkBy(length()). */
void clear();
/* Potentially fallible append operations. */
bool append(const T &t);
bool appendN(const T &t, size_t n);
template <class U> bool append(const U *begin, const U *end);
template <class U> bool append(const U *begin, size_t length);
template <class U, size_t O, class BP> bool append(const Vector<U,O,BP> &other);
/*
* Guaranteed-infallible append operations for use upon vectors whose
* memory has been pre-reserved.
*/
void infallibleAppend(const T &t) {
JS_ASSERT(mLength + 1 <= reserved());
JS_ALWAYS_TRUE(append(t));
}
void infallibleAppendN(const T &t, size_t n) {
JS_ASSERT(mLength + n <= reserved());
JS_ALWAYS_TRUE(appendN(t, n));
}
template <class U> void infallibleAppend(const U *begin, const U *end) {
JS_ASSERT(mLength + PointerRangeSize(begin, end) <= reserved());
JS_ALWAYS_TRUE(append(begin, end));
}
template <class U> void infallibleAppend(const U *begin, size_t length) {
JS_ASSERT(mLength + length <= reserved());
JS_ALWAYS_TRUE(append(begin, length));
}
template <class U, size_t O, class BP> void infallibleAppend(const Vector<U,O,BP> &other) {
JS_ASSERT(mLength + other.length() <= reserved());
JS_ALWAYS_TRUE(append(other));
}
void popBack();
T popCopy();
@ -384,6 +427,8 @@ class Vector : AllocPolicy
#define REENTRANCY_GUARD_ET_AL \
ReentrancyGuard g(*this); \
JS_ASSERT_IF(usingInlineStorage(), mCapacity == sInlineCapacity); \
JS_ASSERT(reserved() <= mCapacity); \
JS_ASSERT(mLength <= reserved()); \
JS_ASSERT(mLength <= mCapacity)
/* Vector Implementation */
@ -394,7 +439,7 @@ Vector<T,N,AllocPolicy>::Vector(AllocPolicy ap)
: AllocPolicy(ap), mBegin((T *)storage.addr()), mLength(0),
mCapacity(sInlineCapacity)
#ifdef DEBUG
, entered(false)
, mReserved(0), entered(false)
#endif
{}
@ -503,9 +548,16 @@ inline bool
Vector<T,N,AP>::reserve(size_t request)
{
REENTRANCY_GUARD_ET_AL;
if (request > mCapacity)
return growStorageBy(request - mLength);
return true;
if (request <= mCapacity || growStorageBy(request - mLength)) {
#ifdef DEBUG
if (request > mReserved)
mReserved = request;
JS_ASSERT(mLength <= mReserved);
JS_ASSERT(mReserved <= mCapacity);
#endif
return true;
}
return false;
}
template <class T, size_t N, class AP>
@ -532,6 +584,10 @@ Vector<T,N,AP>::growByImpl(size_t incr)
if (InitNewElems)
Impl::initialize(endNoCheck(), newend);
mLength += incr;
#ifdef DEBUG
if (mLength > mReserved)
mReserved = mLength;
#endif
return true;
}
@ -592,6 +648,10 @@ Vector<T,N,AP>::append(const T &t)
JS_ASSERT(mLength < mCapacity);
new(endNoCheck()) T(t);
++mLength;
#ifdef DEBUG
if (mLength > mReserved)
mReserved = mLength;
#endif
return true;
}
@ -606,6 +666,10 @@ Vector<T,N,AP>::appendN(const T &t, size_t needed)
JS_ASSERT(mLength + needed <= mCapacity);
Impl::copyConstructN(endNoCheck(), needed, t);
mLength += needed;
#ifdef DEBUG
if (mLength > mReserved)
mReserved = mLength;
#endif
return true;
}
@ -655,6 +719,10 @@ Vector<T,N,AP>::append(const U *insBegin, const U *insEnd)
JS_ASSERT(mLength + needed <= mCapacity);
Impl::copyConstruct(endNoCheck(), insBegin, insEnd);
mLength += needed;
#ifdef DEBUG
if (mLength > mReserved)
mReserved = mLength;
#endif
return true;
}
@ -711,6 +779,9 @@ Vector<T,N,AP>::extractRawBuffer()
mBegin = (T *)storage.addr();
mLength = 0;
mCapacity = sInlineCapacity;
#ifdef DEBUG
mReserved = 0;
#endif
}
return ret;
}
@ -744,6 +815,9 @@ Vector<T,N,AP>::replaceRawBuffer(T *p, size_t length)
mLength = length;
mCapacity = length;
}
#ifdef DEBUG
mReserved = length;
#endif
}
} /* namespace js */

View File

@ -1057,14 +1057,12 @@ ResolveRelativePath(JSContext *cx, const char *base, JSString *filename)
size_t nchars;
if (!JS_DecodeBytes(cx, base, dirLen + 1, NULL, &nchars))
return NULL;
if (!result.reserve(dirLen + 1 + fileLen)) {
JS_ReportOutOfMemory(cx);
if (!result.reserve(dirLen + 1 + fileLen))
return NULL;
}
JS_ALWAYS_TRUE(result.resize(dirLen + 1));
if (!JS_DecodeBytes(cx, base, dirLen + 1, result.begin(), &nchars))
return NULL;
JS_ALWAYS_TRUE(result.append(fileChars, fileLen));
result.infallibleAppend(fileChars, fileLen);
return JS_NewUCStringCopyN(cx, result.begin(), result.length());
}