diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index bb451ed28b1b..ed8dd969f134 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -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) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 1fa80c228a07..6cfcb2034328 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -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); } } diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 41469414e8e1..5b168685219c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -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()); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 4c9a2cca7318..5219aa071fd9 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -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) { diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 93aeab36a5ef..dde3c035cbd0 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -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)); } diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index adefa252b3da..542ea105b3db 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -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); diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index 646d4f62c7aa..4d68525d991f 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -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 diff --git a/js/src/jsregexp.h b/js/src/jsregexp.h index 1c1d061095f4..ae72dc2eb64e 100644 --- a/js/src/jsregexp.h +++ b/js/src/jsregexp.h @@ -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; diff --git a/js/src/jsregexpinlines.h b/js/src/jsregexpinlines.h index 30c72d9d401b..b4ac02823af2 100644 --- a/js/src/jsregexpinlines.h +++ b/js/src/jsregexpinlines.h @@ -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) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index e8dd037920fa..6cf23b9bc13d 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -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; diff --git a/js/src/jsstrinlines.h b/js/src/jsstrinlines.h index 8742df31cdc9..0a45e5098eac 100644 --- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -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); diff --git a/js/src/jsvector.h b/js/src/jsvector.h index ab432bae7647..04216e66dea1 100644 --- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -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 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 bool append(const U *begin, const U *end); template bool append(const U *begin, size_t length); template bool append(const Vector &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 void infallibleAppend(const U *begin, const U *end) { + JS_ASSERT(mLength + PointerRangeSize(begin, end) <= reserved()); + JS_ALWAYS_TRUE(append(begin, end)); + } + template void infallibleAppend(const U *begin, size_t length) { + JS_ASSERT(mLength + length <= reserved()); + JS_ALWAYS_TRUE(append(begin, length)); + } + template void infallibleAppend(const Vector &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::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::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 @@ -532,6 +584,10 @@ Vector::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::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::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::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::extractRawBuffer() mBegin = (T *)storage.addr(); mLength = 0; mCapacity = sInlineCapacity; +#ifdef DEBUG + mReserved = 0; +#endif } return ret; } @@ -744,6 +815,9 @@ Vector::replaceRawBuffer(T *p, size_t length) mLength = length; mCapacity = length; } +#ifdef DEBUG + mReserved = length; +#endif } } /* namespace js */ diff --git a/js/src/shell/jsworkers.cpp b/js/src/shell/jsworkers.cpp index 4053cbb3c617..340d3d3d9be6 100644 --- a/js/src/shell/jsworkers.cpp +++ b/js/src/shell/jsworkers.cpp @@ -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()); }