From e94edc5f036544c0d6021eaf1dfb003529088d44 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 7 Aug 2009 20:09:11 -0700 Subject: [PATCH] Bug 503952 - JSStringBuffer/JSCharBuffer with JSTempVector, part 1. r=jwalden --- js/src/jsarray.cpp | 158 ++++----- js/src/jsarray.h | 4 +- js/src/jsbool.cpp | 7 +- js/src/jsbool.h | 2 +- js/src/jsnum.cpp | 2 +- js/src/jsnum.h | 2 +- js/src/jsprvtd.h | 7 +- js/src/jsregexp.cpp | 107 ++++--- js/src/jsstr.cpp | 32 +- js/src/jsstr.h | 14 +- js/src/jsvector.h | 764 +++++++++++++++++++++++++++++++++----------- 11 files changed, 761 insertions(+), 338 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index df3dd1caf7e0..c91a535b4af1 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1331,58 +1331,51 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj) } /* Transfer ownership of buffer to returned string. */ -static JSBool -BufferToString(JSContext *cx, JSTempVector &buf, jsval *rval) +static inline JSBool +BufferToString(JSContext *cx, JSCharVector &buf, jsval *rval) { - size_t length = buf.size() - 1; - jschar *chars = buf.extractRawBuffer(); - JSString *str = js_NewString(cx, chars, length); - if (!str) { - cx->free(chars); - return JS_FALSE; - } + JSString *str = js_NewStringFromCharBuffer(cx, buf); + if (!str) + return false; *rval = STRING_TO_JSVAL(str); - return JS_TRUE; + return true; } #if JS_HAS_TOSOURCE static JSBool array_toSource(JSContext *cx, uintN argc, jsval *vp) { - JS_CHECK_RECURSION(cx, return JS_FALSE); + JS_CHECK_RECURSION(cx, return false); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj || (OBJ_GET_CLASS(cx, obj) != &js_SlowArrayClass && !JS_InstanceOf(cx, obj, &js_ArrayClass, vp + 2))) { - return JS_FALSE; + return false; } /* Find joins or cycles in the reachable object graph. */ jschar *sharpchars; JSHashEntry *he = js_EnterSharpObject(cx, obj, NULL, &sharpchars); if (!he) - return JS_FALSE; - JSBool initiallySharp = IS_SHARP(he) ? JS_TRUE : JS_FALSE; + return false; + bool initiallySharp = IS_SHARP(he); - /* After this point, all paths exit through the 'done' label. */ - MUST_FLOW_THROUGH("done"); - JSBool ok = JS_TRUE; + /* After this point, all paths exit through the 'out' label. */ + MUST_FLOW_THROUGH("out"); + bool ok = false; /* * This object will take responsibility for the jschar buffer until the * buffer is transferred to the returned JSString. */ - JSTempVector buf(cx); - if (!(ok = buf.reserve(3))) - goto done; + JSCharVector buf(cx); /* Cycles/joins are indicated by sharp objects. */ #if JS_HAS_SHARP_VARS if (IS_SHARP(he)) { JS_ASSERT(sharpchars != 0); - /* +1 to include the trailing '\0' */ - buf.replaceRawBuffer(sharpchars, js_strlen(sharpchars) + 1); + buf.replaceRawBuffer(sharpchars, js_strlen(sharpchars)); goto make_string; } else if (sharpchars) { MAKE_SHARP(he); @@ -1390,30 +1383,28 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp) } #else if (IS_SHARP(he)) { - static const jschar arr[] = { '[', ']', 0 }; - if (!(ok = buf.pushBack(arr, arr + 3))) - goto done; + if (!js_AppendLiteral(buf, "[]")) + goto out; if (sharpchars) cx->free(sharpchars); goto make_string; } #endif - if (!(ok = buf.pushBack('['))) - goto done; + if (!buf.append('[')) + goto out; jsuint length; - ok = js_GetLengthProperty(cx, obj, &length); - if (!ok) - goto done; + if (!js_GetLengthProperty(cx, obj, &length)) + goto out; for (jsuint index = 0; index < length; index++) { /* Use vp to locally root each element value. */ JSBool hole; - ok = (JS_CHECK_OPERATION_LIMIT(cx) && - GetArrayElement(cx, obj, index, &hole, vp)); - if (!ok) - goto done; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, index, &hole, vp)) { + goto out; + } /* Get element's character string. */ JSString *str; @@ -1421,10 +1412,8 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp) str = cx->runtime->emptyString; } else { str = js_ValueToSource(cx, *vp); - if (!str) { - ok = JS_FALSE; - goto done; - } + if (!str) + goto out; } *vp = STRING_TO_JSVAL(str); const jschar *chars; @@ -1432,26 +1421,28 @@ array_toSource(JSContext *cx, uintN argc, jsval *vp) str->getCharsAndLength(chars, charlen); /* Append element to buffer. */ - if (!(ok = buf.pushBack(chars, chars + charlen))) - goto done; + if (!buf.append(chars, charlen)) + goto out; if (index + 1 != length) { - if (!(ok = buf.pushBack(',')) || !(ok = buf.pushBack(' '))) - goto done; + if (!js_AppendLiteral(buf, ", ")) + goto out; } else if (hole) { - if (!(ok = buf.pushBack(','))) - goto done; + if (!buf.append(',')) + goto out; } } /* Finalize the buffer. */ - if (!(ok = buf.pushBack(']')) || !(ok = buf.pushBack(0))) - goto done; + if (!buf.append(']')) + goto out; make_string: - if (!(ok = BufferToString(cx, buf, vp))) - goto done; + if (!BufferToString(cx, buf, vp)) + goto out; - done: + ok = true; + + out: if (!initiallySharp) js_LeaveSharpObject(cx, NULL); return ok; @@ -1464,7 +1455,7 @@ js_hash_array(const void *key) return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; } -JSBool +bool js_InitContextBusyArrayTable(JSContext *cx) { cx->busyArrayTable = JS_NewHashTable(4, js_hash_array, JS_CompareValues, @@ -1476,7 +1467,7 @@ static JSBool array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, JSString *sepstr, jsval *rval) { - JS_CHECK_RECURSION(cx, return JS_FALSE); + JS_CHECK_RECURSION(cx, return false); /* * This hash table is shared between toString invocations and must be empty @@ -1485,7 +1476,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, JSHashTable *table = cx->busyArrayTable; /* - * Use HashTable entry as the cycle indicator. On first visit, create the + * Use HashTable entry as the cycle indicator. On first visit, create the * entry, and, when leaving, remove the entry. */ JSHashNumber hash = js_hash_array(obj); @@ -1496,19 +1487,19 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); if (!he) { JS_ReportOutOfMemory(cx); - return JS_FALSE; + return false; } } else { /* Cycle, so return empty string. */ *rval = ATOM_KEY(cx->runtime->atomState.emptyAtom); - return JS_TRUE; + return true; } JSAutoTempValueRooter tvr(cx, obj); - /* After this point, all paths exit through the 'done' label. */ - MUST_FLOW_THROUGH("done"); - JSBool ok = JS_TRUE; + /* After this point, all paths exit through the 'out' label. */ + MUST_FLOW_THROUGH("out"); + bool ok = false; /* Get characters to use for the separator. */ static const jschar comma = ','; @@ -1525,64 +1516,55 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, * This object will take responsibility for the jschar buffer until the * buffer is transferred to the returned JSString. */ - JSTempVector buf(cx); + JSCharVector buf(cx); jsuint length; - ok = js_GetLengthProperty(cx, obj, &length); - if (!ok) - goto done; + if (!js_GetLengthProperty(cx, obj, &length)) + goto out; for (jsuint index = 0; index < length; index++) { /* Use rval to locally root each element value. */ JSBool hole; - ok = JS_CHECK_OPERATION_LIMIT(cx) && - GetArrayElement(cx, obj, index, &hole, rval); - if (!ok) - goto done; + if (!JS_CHECK_OPERATION_LIMIT(cx) || + !GetArrayElement(cx, obj, index, &hole, rval)) { + goto out; + } /* Get element's character string. */ if (!(hole || JSVAL_IS_VOID(*rval) || JSVAL_IS_NULL(*rval))) { if (locale) { + /* Work on obj.toLocalString() instead. */ JSObject *robj; + if (!js_ValueToObject(cx, *rval, &robj)) + goto out; + *rval = OBJECT_TO_JSVAL(robj); JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom; - ok = js_ValueToObject(cx, *rval, &robj); - if (ok) { - /* Re-use *rval to protect robj temporarily. */ - *rval = OBJECT_TO_JSVAL(robj); - ok = js_TryMethod(cx, robj, atom, 0, NULL, rval); - } - if (!ok) - goto done; + if (!js_TryMethod(cx, robj, atom, 0, NULL, rval)) + goto out; } - ok = js_ValueToStringBuffer(cx, *rval, buf); - if (!ok) - goto done; + if (!js_ValueToCharBuffer(cx, *rval, buf)) + goto out; } /* Append the separator. */ if (index + 1 != length) { - if (!(ok = buf.pushBack(sep, sep + seplen))) - goto done; + if (!buf.append(sep, seplen)) + goto out; } } /* Finalize the buffer. */ - if (buf.empty()) { - *rval = ATOM_KEY(cx->runtime->atomState.emptyAtom); - goto done; - } + if (!BufferToString(cx, buf, rval)) + goto out; - ok = buf.pushBack(0) && - BufferToString(cx, buf, rval); - if (!ok) - goto done; + ok = true; - done: + out: /* * It is possible that 'hep' may have been invalidated by subsequent - * RawAdd/Remove. Hence, 'RawRemove' must not be used. + * RawAdd/Remove. Hence, 'RawRemove' must not be used. */ JS_HashTableRemove(table, obj); return ok; diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 52a365fe3e91..e1c128bad52b 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -97,8 +97,8 @@ js_GetProtoIfDenseArray(JSContext *cx, JSObject *obj) extern JSObject * js_InitArrayClass(JSContext *cx, JSObject *obj); -extern JSBool -js_InitContextBusyArrayTable(JSContext *); +extern bool +js_InitContextBusyArrayTable(JSContext *cx); extern JSObject * js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector, diff --git a/js/src/jsbool.cpp b/js/src/jsbool.cpp index 4dc05bc7026f..c136d3214452 100644 --- a/js/src/jsbool.cpp +++ b/js/src/jsbool.cpp @@ -164,12 +164,9 @@ js_BooleanToString(JSContext *cx, JSBool b) /* This function implements E-262-3 section 9.8, toString. */ JSBool -js_BooleanToStringBuffer(JSContext *cx, JSBool b, JSTempVector &buf) +js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharVector &buf) { - static const jschar trueChars[] = { 't', 'r', 'u', 'e' }, - falseChars[] = { 'f', 'a', 'l', 's', 'e' }; - return b ? buf.pushBack(trueChars, trueChars + JS_ARRAY_LENGTH(trueChars)) - : buf.pushBack(falseChars, falseChars + JS_ARRAY_LENGTH(falseChars)); + return b ? js_AppendLiteral(buf, "true") : js_AppendLiteral(buf, "false"); } JSBool diff --git a/js/src/jsbool.h b/js/src/jsbool.h index 20d6db999625..5e63b8d9b327 100644 --- a/js/src/jsbool.h +++ b/js/src/jsbool.h @@ -81,7 +81,7 @@ extern JSString * js_BooleanToString(JSContext *cx, JSBool b); extern JSBool -js_BooleanToStringBuffer(JSContext *cx, JSBool b, JSTempVector &buf); +js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharVector &buf); extern JSBool js_ValueToBoolean(jsval v); diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 32077b149871..1bbb575d4a88 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -862,7 +862,7 @@ js_NumberToString(JSContext *cx, jsdouble d) } JSBool JS_FASTCALL -js_NumberValueToStringBuffer(JSContext *cx, jsval v, JSTempVector &buf) +js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf) { /* Convert to C-string. */ static const size_t arrSize = DTOSTR_STANDARD_BUFFER_SIZE; diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 5914ce9b2178..f5fdfef637b6 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -194,7 +194,7 @@ js_NumberToString(JSContext *cx, jsdouble d); * append to the given buffer. */ extern JSBool JS_FASTCALL -js_NumberValueToStringBuffer(JSContext *, jsval, JSTempVector &); +js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb); /* * Convert a value to a number. On exit JSVAL_IS_NULL(*vp) iff there was an diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 6cd9355d6eb0..1f1b7437a17a 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -137,14 +137,17 @@ typedef struct JSXMLArrayCursor JSXMLArrayCursor; /* * Template declarations. * - * jsprvtd.h can be included in both C and C++ translation units. For C++, it + * jsprvtd.h can be included in both C and C++ translation units. For C++, it * may possibly be wrapped in an extern "C" block which does not agree with * templates. */ #ifdef __cplusplus extern "C++" { -template class JSTempVector; +template class JSTempVector; + +/* Common JSTempVector instantiations: */ +typedef JSTempVector JSCharVector; } #endif /* __cplusplus */ diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index ef4317ab9fea..9f24030ec6de 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -2062,8 +2062,8 @@ namespace { /* * This table allows efficient testing for the ASCII portion of \s during a - * trace. ECMA-262 15.10.2.12 defines the following characters below 128 to be - * whitespace: 0x9 (0), 0xA (10), 0xB (11), 0xC (12), 0xD (13), 0x20 (32). The + * trace. ECMA-262 15.10.2.12 defines the following characters below 128 to be + * whitespace: 0x9 (0), 0xA (10), 0xB (11), 0xC (12), 0xD (13), 0x20 (32). The * index must be <= 32. */ static const bool js_ws[] = { @@ -2157,7 +2157,7 @@ CharSet::disjoint(const jschar *beg, const jschar *end, uintN classes) } /* - * Predicate version of the STL's set_intersection. Assumes both ranges are + * Predicate version of the STL's set_intersection. Assumes both ranges are * sorted and thus runs in linear time. * * FIXME: This is a reusable algorithm, perhaps it should be put somewhere. @@ -2205,9 +2205,9 @@ CharSet::disjoint(const CharSet &other) const } /* - * Return true if the given subexpression may match the empty string. The - * conservative answer is |true|. If |next| is true, then the subexpression is - * considered to be |node| followed by the rest of |node->next|. Otherwise, the + * Return true if the given subexpression may match the empty string. The + * conservative answer is |true|. If |next| is true, then the subexpression is + * considered to be |node| followed by the rest of |node->next|. Otherwise, the * subexpression is considered to be |node| by itself. */ static bool @@ -2232,7 +2232,7 @@ mayMatchEmpty(RENode *node, bool next = true) /* * Enumerate the set of characters that may be consumed next by the given - * subexpression in isolation. Return whether the enumeration was successful. + * subexpression in isolation. Return whether the enumeration was successful. */ static bool enumerateNextChars(JSContext *cx, RENode *node, CharSet &set) @@ -2325,7 +2325,8 @@ class RegExpNativeCompiler { LIns* compileFlatSingleChar(jschar ch, LIns* pos, LInsList& fails) { LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0); - fails.pushBack(to_fail); + if (!fails.append(to_fail)) + return NULL; LIns* text_ch = lir->insLoad(LIR_ldcs, pos, 0); // Extra characters that need to be compared against when doing folding. @@ -2410,7 +2411,8 @@ class RegExpNativeCompiler { extras[i].match = branch; } - fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, text_ch, lir->insImm(ch)), 0)); + if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, text_ch, lir->insImm(ch)), 0))) + return NULL; for (int i = 0; i < nextras; ++i) targetCurrentPoint(extras[i].match); @@ -2456,12 +2458,14 @@ class RegExpNativeCompiler { } LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, lir->ins2(LIR_sub, cpend, lir->insImm(2))), 0); - fails.pushBack(to_fail); + if (!fails.append(to_fail)) + return NULL; LIns* text_word = lir->insLoad(LIR_ld, pos, 0); LIns* comp_word = useFastCI ? lir->ins2(LIR_or, text_word, lir->insImm(mask.i)) : text_word; - fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, comp_word, lir->insImm(word)), 0)); + if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, comp_word, lir->insImm(word)), 0))) + return NULL; return lir->ins2(LIR_piadd, pos, lir->insImm(4)); } @@ -2537,9 +2541,14 @@ class RegExpNativeCompiler { memcpy(bitmapData, charSet->u.bits, bitmapLen); LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0); - fails.pushBack(to_fail); + if (!fails.append(to_fail)) + return NULL; LIns* text_ch = lir->insLoad(LIR_ldcs, pos, 0); - fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_le, text_ch, lir->insImm(charSet->length)), 0)); + if (!fails.append(lir->insBranch(LIR_jf, + lir->ins2(LIR_le, text_ch, lir->insImm(charSet->length)), + 0))) { + return NULL; + } LIns* byteIndex = lir->ins2(LIR_rsh, text_ch, lir->insImm(3)); LIns* bitmap = lir->insImmPtr(bitmapData); LIns* byte = lir->insLoad(LIR_ldcb, lir->ins2(LIR_piadd, bitmap, byteIndex), (int) 0); @@ -2548,7 +2557,8 @@ class RegExpNativeCompiler { LIns* test = lir->ins2(LIR_eq, lir->ins2(LIR_and, byte, bitMask), lir->insImm(0)); LIns* to_next = lir->insBranch(LIR_jt, test, 0); - fails.pushBack(to_next); + if (!fails.append(to_next)) + return NULL; return lir->ins2(LIR_piadd, pos, lir->insImm(2)); } @@ -2567,7 +2577,8 @@ class RegExpNativeCompiler { LIns *compileBuiltinClass(RENode *node, LIns *pos, LInsList &fails) { /* All the builtins checked below consume one character. */ - fails.pushBack(lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0)); + if (!fails.append(lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0))) + return NULL; LIns *chr = lir->insLoad(LIR_ldcs, pos, 0); switch (node->op) { @@ -2575,21 +2586,27 @@ class RegExpNativeCompiler { { /* Accept any character except those in ECMA-262 15.10.2.8. */ LIns *eq1 = lir->ins2(LIR_eq, chr, lir->insImm('\n')); - fails.pushBack(lir->insBranch(LIR_jt, eq1, NULL)); + if (!fails.append(lir->insBranch(LIR_jt, eq1, NULL))) + return NULL; LIns *eq2 = lir->ins2(LIR_eq, chr, lir->insImm('\r')); - fails.pushBack(lir->insBranch(LIR_jt, eq2, NULL)); + if (!fails.append(lir->insBranch(LIR_jt, eq2, NULL))) + return NULL; LIns *eq3 = lir->ins2(LIR_eq, chr, lir->insImm(LINE_SEPARATOR)); - fails.pushBack(lir->insBranch(LIR_jt, eq3, NULL)); + if (!fails.append(lir->insBranch(LIR_jt, eq3, NULL))) + return NULL; LIns *eq4 = lir->ins2(LIR_eq, chr, lir->insImm(PARA_SEPARATOR)); - fails.pushBack(lir->insBranch(LIR_jt, eq4, NULL)); + if (!fails.append(lir->insBranch(LIR_jt, eq4, NULL))) + return NULL; break; } case REOP_DIGIT: { LIns *ge = lir->ins2(LIR_ge, chr, lir->insImm('0')); - fails.pushBack(lir->insBranch(LIR_jf, ge, NULL)); + if (!fails.append(lir->insBranch(LIR_jf, ge, NULL))) + return NULL; LIns *le = lir->ins2(LIR_le, chr, lir->insImm('9')); - fails.pushBack(lir->insBranch(LIR_jf, le, NULL)); + if (!fails.append(lir->insBranch(LIR_jf, le, NULL))) + return NULL; break; } case REOP_NONDIGIT: @@ -2598,7 +2615,8 @@ class RegExpNativeCompiler { LIns *ge = lir->ins2(LIR_ge, chr, lir->insImm('0')); LIns *le = lir->ins2(LIR_le, chr, lir->insImm('9')); LIns *both = lir->ins2(LIR_and, ge, le); - fails.pushBack(lir->insBranch(LIR_jf, lir->ins_eq0(both), NULL)); + if (!fails.append(lir->insBranch(LIR_jf, lir->ins_eq0(both), NULL))) + return NULL; break; } case REOP_ALNUM: @@ -2608,9 +2626,11 @@ class RegExpNativeCompiler { * ((uint)*cp) < 128 && js_alnum[(uint)*cp] */ LIns *rangeCnd = lir->ins2(LIR_ult, chr, lir->insImm(128)); - fails.pushBack(lir->insBranch(LIR_jf, rangeCnd, NULL)); + if (!fails.append(lir->insBranch(LIR_jf, rangeCnd, NULL))) + return NULL; LIns *tableVal = compileTableRead(chr, js_alnum); - fails.pushBack(lir->insBranch(LIR_jt, lir->ins_eq0(tableVal), NULL)); + if (!fails.append(lir->insBranch(LIR_jt, lir->ins_eq0(tableVal), NULL))) + return NULL; break; } case REOP_NONALNUM: @@ -2622,7 +2642,8 @@ class RegExpNativeCompiler { LIns *rangeCnd = lir->ins2(LIR_uge, chr, lir->insImm(128)); LIns *rangeBr = lir->insBranch(LIR_jt, rangeCnd, NULL); LIns *tableVal = compileTableRead(chr, js_alnum); - fails.pushBack(lir->insBranch(LIR_jf, lir->ins_eq0(tableVal), NULL)); + if (!fails.append(lir->insBranch(LIR_jf, lir->ins_eq0(tableVal), NULL))) + return NULL; LIns *success = lir->ins0(LIR_label); rangeBr->setTarget(success); break; @@ -2632,9 +2653,9 @@ class RegExpNativeCompiler { { /* * ECMA-262 7.2, 7.3, and 15.10.2.12 define a bunch of Unicode code - * points for whitespace. We optimize here for the common case of + * points for whitespace. We optimize here for the common case of * ASCII characters using a table lookup for the lower block that - * can actually contain spaces. For the rest, use a (more or less) + * can actually contain spaces. For the rest, use a (more or less) * binary search to minimize tests. * * [0000,0020]: 9, A, B, C, D, 20 @@ -2699,8 +2720,10 @@ class RegExpNativeCompiler { belowMissBr->setTarget(missLbl); aboveMissBr->setTarget(missLbl); LIns *missBr = lir->ins2(LIR_j, NULL, NULL); - if (node->op == REOP_SPACE) - fails.pushBack(missBr); + if (node->op == REOP_SPACE) { + if (!fails.append(missBr)) + return NULL; + } /* Collect matches. */ LIns *matchLbl = lir->ins0(LIR_label); @@ -2712,7 +2735,8 @@ class RegExpNativeCompiler { eq7Br->setTarget(matchLbl); eq8Br->setTarget(matchLbl); if (node->op == REOP_NONSPACE) { LIns *matchBr = lir->ins2(LIR_j, NULL, NULL); - fails.pushBack(matchBr); + if (!fails.append(matchBr)) + return NULL; } /* Fall through means match == success. */ @@ -2735,7 +2759,7 @@ class RegExpNativeCompiler { /* * If the RE continues after the alternative, we need to ensure that no - * backtracking is required. Recursive calls to compileNode will fail + * backtracking is required. Recursive calls to compileNode will fail * on capturing parens, so the only thing we have to check here is that, * if the left subexpression matches, we can keep going without later * deciding we need to try the right subexpression. @@ -2835,7 +2859,7 @@ class RegExpNativeCompiler { /* * If the RE continues after the alternative, we need to ensure that no - * backtracking is required. Recursive calls to compileNode will fail + * backtracking is required. Recursive calls to compileNode will fail * on capturing parens, so the only thing we have to check here is that, * if the quantifier body matches, we can continue matching the body * without later deciding we need to undo the body matches. @@ -2892,7 +2916,8 @@ class RegExpNativeCompiler { */ if (mayMatchEmpty(bodyRe)) { LIns *eqCnd = lir->ins2(LIR_eq, iterBegin, iterEnd); - kidFails.pushBack(lir->insBranch(LIR_jt, eqCnd, NULL)); + if (!kidFails.append(lir->insBranch(LIR_jt, eqCnd, NULL))) + return NULL; } /* End iteration: store loop variables, increment, jump */ @@ -2901,17 +2926,17 @@ class RegExpNativeCompiler { /* * This might be the only LIR_live in Mozilla, so I will explain its - * sinister semantics. LIR_lives must appear immediately following a + * sinister semantics. LIR_lives must appear immediately following a * backwards jump and describe what is live immediately at the *target* - * of the back-edge. Thus, these instructions answer the question "what + * of the back-edge. Thus, these instructions answer the question "what * is live at the top of the loop?", which makes sense, because the * backwards scan has not yet seen the top of the loop and needs this * information to continue working backwards up the inside of the loop. * * Here, 'cpend' and 'state' get defined before the loop, and used - * inside, so they are live at 'loopTop'. While 'iterBegin' is used - * after the loop, making it live in on loop exit, it gets defined after - * 'loopTop', which "kills" its liveness. + * inside, so they are live at 'loopTop'. While 'iterBegin' is used + * after the loop, making it live in on loop exit, it gets defined + * after 'loopTop', which "kills" its liveness. */ lir->ins1(LIR_live, state); lir->ins1(LIR_live, cpend); @@ -2922,8 +2947,8 @@ class RegExpNativeCompiler { } /* - * Compile the regular expression rooted at 'node'. Return 0 on failed - * compilation. Otherwise, generate code that falls through on success (the + * Compile the regular expression rooted at 'node'. Return 0 on failed + * compilation. Otherwise, generate code that falls through on success (the * returned LIns* is the current 'pos') and jumps to the end on failure (by * adding the guard LIns to 'fails'). */ @@ -3010,7 +3035,7 @@ class RegExpNativeCompiler { /* Compile normal regular expressions that can match starting at any char. */ bool compileAnchoring(RENode *root, LIns *start) { - /* Guard outer anchoring loop. Use <= to allow empty regexp match. */ + /* Guard outer anchoring loop. Use <= to allow empty regexp match. */ LIns *anchorFail = lir->insBranch(LIR_jf, lir->ins2(LIR_le, start, cpend), 0); if (!compileRootNode(root, start, anchorFail)) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 33f553ab391b..12a8bdba8ea4 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2762,6 +2762,26 @@ js_NewString(JSContext *cx, jschar *chars, size_t length) return str; } +JSString * +js_NewStringFromCharBuffer(JSContext *cx, JSCharVector &cb) +{ + if (cb.empty()) + return ATOM_TO_STRING(cx->runtime->atomState.emptyAtom); + + size_t length = cb.size(); + if (!cb.append('\0')) + return NULL; + + jschar *buf = cb.extractRawBuffer(); + if (!buf) + return NULL; + + JSString *str = js_NewString(cx, buf, length); + if (!str) + cx->free(buf); + return str; +} + JSString * js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length) @@ -2922,18 +2942,18 @@ js_ValueToString(JSContext *cx, jsval v) } static inline JSBool -pushAtom(JSAtom *atom, JSTempVector &buf) +pushAtom(JSAtom *atom, JSCharVector &buf) { JSString *str = ATOM_TO_STRING(atom); const jschar *chars; size_t length; str->getCharsAndLength(chars, length); - return buf.pushBack(chars, chars + length); + return buf.append(chars, length); } /* This function implements E-262-3 section 9.8, toString. */ JS_FRIEND_API(JSBool) -js_ValueToStringBuffer(JSContext *cx, jsval v, JSTempVector &buf) +js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf) { if (!JSVAL_IS_PRIMITIVE(v) && !OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_STRING, &v)) { @@ -2945,12 +2965,12 @@ js_ValueToStringBuffer(JSContext *cx, jsval v, JSTempVector &buf) const jschar *chars; size_t length; str->getCharsAndLength(chars, length); - return buf.pushBack(chars, chars + length); + return buf.append(chars, length); } if (JSVAL_IS_NUMBER(v)) - return js_NumberValueToStringBuffer(cx, v, buf); + return js_NumberValueToCharBuffer(cx, v, buf); if (JSVAL_IS_BOOLEAN(v)) - return js_BooleanToStringBuffer(cx, JSVAL_TO_BOOLEAN(v), buf); + return js_BooleanToCharBuffer(cx, JSVAL_TO_BOOLEAN(v), buf); if (JSVAL_IS_NULL(v)) return pushAtom(cx->runtime->atomState.nullAtom, buf); JS_ASSERT(JSVAL_IS_VOID(v)); diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 6e5b77e8d422..d83ad736a626 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -574,6 +574,14 @@ extern const char js_encodeURIComponent_str[]; extern JSString * js_NewString(JSContext *cx, jschar *chars, size_t length); +/* + * GC-allocate a string descriptor and steal the char buffer held by |cb|. + * This function takes responsibility for adding the terminating '\0' required + * by js_NewString. + */ +extern JSString * +js_NewStringFromCharBuffer(JSContext *cx, JSCharVector &cb); + extern JSString * js_NewDependentString(JSContext *cx, JSString *base, size_t start, size_t length); @@ -608,12 +616,12 @@ extern JS_FRIEND_API(JSString *) js_ValueToString(JSContext *cx, jsval v); /* - * This function implements E-262-3 section 9.8, toString. Convert the given - * value to a string of jschars appended to the given buffer. On error, the + * This function implements E-262-3 section 9.8, toString. Convert the given + * value to a string of jschars appended to the given buffer. On error, the * passed buffer may have partial results appended. */ extern JS_FRIEND_API(JSBool) -js_ValueToStringBuffer(JSContext *, jsval, JSTempVector &); +js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb); /* * Convert a value to its source expression, returning null after reporting diff --git a/js/src/jsvector.h b/js/src/jsvector.h index 2be1f2dbc33d..4b574cfc1865 100644 --- a/js/src/jsvector.h +++ b/js/src/jsvector.h @@ -40,29 +40,74 @@ #ifndef jsvector_h_ #define jsvector_h_ -#include "jscntxt.h" - #include #include +#include "jsbit.h" + +/* Library of template meta-programs for use in the C++ JS data-structures. */ +namespace JSUtils { + +/* Statically compute min/max. */ +template struct min { + static const size_t result = i < j ? i : j; +}; +template struct max { + static const size_t result = i > j ? i : j; +}; + +/* Statically compute floor(log2(i)). */ +template struct FloorLog2 { + static const size_t result = 1 + FloorLog2::result; +}; +template <> struct FloorLog2<0> { /* Error */ }; +template <> struct FloorLog2<1> { static const size_t result = 0; }; + +/* Statically compute ceiling(log2(i)). */ +template struct CeilingLog2 { + static const size_t result = FloorLog2<2 * i - 1>::result; +}; + +/* Statically compute the number of bits in the given unsigned type. */ +template struct BitSize { + static const size_t result = sizeof(T) * JS_BITS_PER_BYTE; +}; + /* - * Traits class for identifying POD types. Until C++0x, there is no automatic + * For the unsigned integral type size_t, compute a mask M for N such that + * for all X, !(X & M) implies X * N will not overflow (w.r.t size_t) + */ +template struct MulOverflowMask { + static const size_t result = + ~((1u << (BitSize::result - CeilingLog2::result)) - 1); +}; +template <> struct MulOverflowMask<0> { /* Error */ }; +template <> struct MulOverflowMask<1> { static const size_t result = 0; }; + +/* + * Traits class for identifying POD types. Until C++0x, there is no automatic * way to detect PODs, so for the moment it is done manually. */ -template struct IsPodType { static const bool result = false; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; -template <> struct IsPodType { static const bool result = true; }; +template struct IsPodType { static const bool result = false; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; +template <> struct IsPodType { static const bool result = true; }; + +} // end namespace JSUtils /* * This template class provides a default implementation for vector operations * when the element type is not known to be a POD, as judged by IsPodType. */ -template +template struct JSTempVectorImpl { /* Destroys constructed objects in the range [begin, end). */ @@ -82,29 +127,38 @@ struct JSTempVectorImpl * [dst, dst+(srcend-srcbeg)) from the range [srcbeg, srcend). */ template - static inline void copyInitialize(T *dst, const U *srcbeg, const U *srcend) { + static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { for (const U *p = srcbeg; p != srcend; ++p, ++dst) new(dst) T(*p); } /* - * Grows the given buffer to have capacity newcap, preserving the objects - * constructed in the range [begin, end) and updating vec. + * Copy-constructs objects in the uninitialized range [dst, dst+n) from the + * same object u. */ - static inline bool growTo(JSTempVector &vec, size_t newcap) { - size_t bytes = sizeof(T) * newcap; - T *newbuf = reinterpret_cast(malloc(bytes)); - if (!newbuf) { - js_ReportOutOfMemory(vec.mCx); + template + static inline void copyConstructN(T *dst, size_t n, const U &u) { + for (T *end = dst + n; dst != end; ++dst) + new(dst) T(u); + } + + /* + * Grows the given buffer to have capacity newcap, preserving the objects + * constructed in the range [begin, end) and updating v. Assumes that (1) + * newcap has not overflowed, and (2) multiplying newcap by sizeof(T) will + * not overflow. + */ + static inline bool growTo(JSTempVector &v, size_t newcap) { + T *newbuf = reinterpret_cast(v.mCx->malloc(newcap * sizeof(T))); + if (!newbuf) return false; - } - for (T *dst = newbuf, *src = vec.mBegin; src != vec.mEnd; ++dst, ++src) + for (T *dst = newbuf, *src = v.heapBegin(); src != v.heapEnd(); ++dst, ++src) new(dst) T(*src); - JSTempVectorImpl::destroy(vec.mBegin, vec.mEnd); - free(vec.mBegin); - vec.mEnd = newbuf + (vec.mEnd - vec.mBegin); - vec.mBegin = newbuf; - vec.mCapacity = newbuf + newcap; + JSTempVectorImpl::destroy(v.heapBegin(), v.heapEnd()); + v.mCx->free(v.heapBegin()); + v.heapEnd() = newbuf + (v.heapEnd() - v.heapBegin()); + v.heapBegin() = newbuf; + v.heapCapacity() = newcap; return true; } }; @@ -114,33 +168,51 @@ struct JSTempVectorImpl * vector operations when the element type is known to be a POD, as judged by * IsPodType. */ -template -struct JSTempVectorImpl +template +struct JSTempVectorImpl { static inline void destroy(T *, T *) {} static inline void initialize(T *begin, T *end) { - //memset(begin, 0, sizeof(T) * (end-begin)); //SLOWER + /* + * You would think that memset would be a big win (or even break even) + * when we know T is a POD. But currently it's not. This is probably + * because |append| tends to be given small ranges and memset requires + * a function call that doesn't get inlined. + * + * memset(begin, 0, sizeof(T) * (end-begin)); + */ for (T *p = begin; p != end; ++p) - *p = 0; + new(p) T(); } - static inline void copyInitialize(T *dst, const T *srcbeg, const T *srcend) { - //memcpy(dst, srcbeg, sizeof(T) * (srcend-srcbeg)); //SLOWER - for (const T *p = srcbeg; p != srcend; ++p, ++dst) + template + static inline void copyConstruct(T *dst, const U *srcbeg, const U *srcend) { + /* + * See above memset comment. Also, notice that copyConstruct is + * currently templated (T != U), so memcpy won't work without + * requiring T == U. + * + * memcpy(dst, srcbeg, sizeof(T) * (srcend - srcbeg)); + */ + for (const U *p = srcbeg; p != srcend; ++p, ++dst) *dst = *p; } - static inline bool growTo(JSTempVector &vec, size_t newcap) { + static inline void copyConstructN(T *dst, size_t n, const T &t) { + for (T *end = dst + n; dst != end; ++dst) + *dst = t; + } + + static inline bool growTo(JSTempVector &v, size_t newcap) { + JS_ASSERT(!v.usingInlineStorage()); size_t bytes = sizeof(T) * newcap; - T *newbuf = reinterpret_cast(realloc(vec.mBegin, bytes)); - if (!newbuf) { - js_ReportOutOfMemory(vec.mCx); + T *newbuf = reinterpret_cast(v.mCx->realloc(v.heapBegin(), bytes)); + if (!newbuf) return false; - } - vec.mEnd = newbuf + (vec.mEnd - vec.mBegin); - vec.mBegin = newbuf; - vec.mCapacity = newbuf + newcap; + v.heapEnd() = newbuf + (v.heapEnd() - v.heapBegin()); + v.heapBegin() = newbuf; + v.heapCapacity() = newcap; return true; } }; @@ -148,19 +220,125 @@ struct JSTempVectorImpl /* * JS-friendly, STL-like container providing a short-lived, dynamic buffer. * JSTempVector calls the constructors/destructors of all elements stored in - * its internal buffer, so non-PODs may be safely used. + * its internal buffer, so non-PODs may be safely used. Additionally, + * JSTempVector stores the first few elements in-place in its member data + * before resorting to dynamic allocation. The minimum number of elements may + * be specified by the parameter N. * * T requirements: * - default and copy constructible, assignable, destructible * - operations do not throw * * N.B: JSTempVector is not reentrant: T member functions called during - * JSTempVector member functions must not call back into the same - * JSTempVector. + * JSTempVector member functions must not call back into the same object. */ -template +template class JSTempVector { + /* utilities */ + + typedef JSTempVectorImpl::result> Impl; + friend struct JSTempVectorImpl::result>; + + bool growHeapCapacityTo(size_t minCapacity); + bool convertToHeapStorage(size_t minCapacity); + + /* magic constants */ + + static const int sMaxInlineBytes = 1024; + + /* compute constants */ + + /* + * Pointers to the heap-allocated buffer. Only [heapBegin(), heapEnd()) + * hold valid constructed T objects. The range [heapEnd(), heapBegin() + + * heapCapacity()) holds uninitialized memory. + */ + struct BufferPtrs { + T *mBegin, *mEnd; + }; + + /* + * Since a vector either stores elements inline or in a heap-allocated + * buffer, reuse the storage. mSizeOrCapacity serves as the union + * discriminator. In inline mode (when elements are stored in u.mBuf), + * mSizeOrCapacity holds the vector's size. In heap mode (when elements + * are stored in [u.ptrs.mBegin, u.ptrs.mEnd)), mSizeOrCapacity holds the + * vector's capacity. + */ + static const size_t sInlineCapacity = + JSUtils::min::result, + sMaxInlineBytes / sizeof(T)>::result; + + /* member data */ + + JSContext *mCx; + + size_t mSizeOrCapacity; + bool usingInlineStorage() const { return mSizeOrCapacity <= sInlineCapacity; } + + union { + BufferPtrs ptrs; + char mBuf[sInlineCapacity * sizeof(T)]; + } u; + + /* Only valid when usingInlineStorage() */ + size_t &inlineSize() { + JS_ASSERT(usingInlineStorage()); + return mSizeOrCapacity; + } + + size_t inlineSize() const { + JS_ASSERT(usingInlineStorage()); + return mSizeOrCapacity; + } + + T *inlineBegin() const { + JS_ASSERT(usingInlineStorage()); + return (T *)u.mBuf; + } + + T *inlineEnd() const { + JS_ASSERT(usingInlineStorage()); + return ((T *)u.mBuf) + mSizeOrCapacity; + } + + /* Only valid when !usingInlineStorage() */ + size_t heapSize() { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mEnd - u.ptrs.mBegin; + } + + size_t &heapCapacity() { + JS_ASSERT(!usingInlineStorage()); + return mSizeOrCapacity; + } + + T *&heapBegin() { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mBegin; + } + + T *&heapEnd() { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mEnd; + } + + size_t heapCapacity() const { + JS_ASSERT(!usingInlineStorage()); + return mSizeOrCapacity; + } + + T *const &heapBegin() const { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mBegin; + } + + T *const &heapEnd() const { + JS_ASSERT(!usingInlineStorage()); + return u.ptrs.mEnd; + } + #ifdef DEBUG bool mInProgress; #endif @@ -184,77 +362,93 @@ class JSTempVector } }; - public: - JSTempVector(JSContext *cx) - : -#ifdef DEBUG - mInProgress(false), -#endif - mCx(cx), mBegin(0), mEnd(0), mCapacity(0) - {} - ~JSTempVector(); - JSTempVector(const JSTempVector &); JSTempVector &operator=(const JSTempVector &); + public: + JSTempVector(JSContext *cx) + : mCx(cx), mSizeOrCapacity(0) +#ifdef DEBUG + , mInProgress(false) +#endif + {} + ~JSTempVector(); + /* accessors */ - size_t size() const { return mEnd - mBegin; } - size_t capacity() const { return mCapacity - mBegin; } - bool empty() const { return mBegin == mEnd; } - - T &operator[](size_t i) { - JS_ASSERT(!mInProgress && i < size()); - return mBegin[i]; + size_t size() const { + return usingInlineStorage() ? inlineSize() : (heapEnd() - heapBegin()); } - const T &operator[](size_t i) const { - JS_ASSERT(!mInProgress && i < size()); - return mBegin[i]; + bool empty() const { + return usingInlineStorage() ? inlineSize() == 0 : (heapBegin() == heapEnd()); + } + + size_t capacity() const { + return usingInlineStorage() ? sInlineCapacity : heapCapacity(); } T *begin() { JS_ASSERT(!mInProgress); - return mBegin; + return usingInlineStorage() ? inlineBegin() : heapBegin(); } const T *begin() const { JS_ASSERT(!mInProgress); - return mBegin; + return usingInlineStorage() ? inlineBegin() : heapBegin(); } T *end() { JS_ASSERT(!mInProgress); - return mEnd; + return usingInlineStorage() ? inlineEnd() : heapEnd(); } const T *end() const { JS_ASSERT(!mInProgress); - return mEnd; + return usingInlineStorage() ? inlineEnd() : heapEnd(); + } + + T &operator[](size_t i) { + JS_ASSERT(!mInProgress && i < size()); + return begin()[i]; + } + + const T &operator[](size_t i) const { + JS_ASSERT(!mInProgress && i < size()); + return begin()[i]; } T &back() { - JS_ASSERT(!mInProgress); - return *(mEnd - 1); + JS_ASSERT(!mInProgress && !empty()); + return *(end() - 1); } const T &back() const { JS_ASSERT(!mInProgress && !empty()); - return *(mEnd - 1); + return *(end() - 1); } /* mutators */ - bool reserve(size_t); - bool growBy(size_t); + bool reserve(size_t capacity); + bool resize(size_t newSize); + void shrinkBy(size_t incr); + bool growBy(size_t incr); void clear(); - bool pushBack(const T &); - template bool pushBack(const U *begin, const U *end); + 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); + + void popBack(); /* * Transfers ownership of the internal buffer used by JSTempVector to the - * caller. After this call, the JSTempVector is empty. + * caller. After this call, the JSTempVector is empty. Since the returned + * buffer may need to be allocated (if the elements are currently + * stored in-place), the call can fail, returning NULL. + * * N.B. Although a T*, only the range [0, size()) is constructed. */ T *extractRawBuffer(); @@ -264,146 +458,340 @@ class JSTempVector * N.B. This call assumes that there are no uninitialized elements in the * passed array. */ - void replaceRawBuffer(T *, size_t length); - - private: - typedef JSTempVectorImpl::result> Impl; - friend struct JSTempVectorImpl::result>; - - static const int sGrowthFactor = 3; - - bool checkOverflow(size_t newval, size_t oldval, size_t diff) const; - - JSContext *mCx; - T *mBegin, *mEnd, *mCapacity; + void replaceRawBuffer(T *p, size_t length); }; -template -inline -JSTempVector::~JSTempVector() -{ - ReentrancyGuard g(*this); - Impl::destroy(mBegin, mEnd); - free(mBegin); -} - -template -inline bool -JSTempVector::reserve(size_t newsz) -{ - ReentrancyGuard g(*this); - size_t oldcap = capacity(); - if (newsz > oldcap) { - size_t diff = newsz - oldcap; - size_t newcap = diff + oldcap * sGrowthFactor; - return checkOverflow(newcap, oldcap, diff) && - Impl::growTo(*this, newcap); - } - return true; -} - -template -inline bool -JSTempVector::growBy(size_t amount) -{ - /* grow if needed */ - size_t oldsize = size(), newsize = oldsize + amount; - if (!checkOverflow(newsize, oldsize, amount) || - (newsize > capacity() && !reserve(newsize))) - return false; - - /* initialize new elements */ - ReentrancyGuard g(*this); - JS_ASSERT(mCapacity - (mBegin + newsize) >= 0); - T *newend = mBegin + newsize; - Impl::initialize(mEnd, newend); - mEnd = newend; - return true; -} - -template -inline void -JSTempVector::clear() -{ - ReentrancyGuard g(*this); - Impl::destroy(mBegin, mEnd); - mEnd = mBegin; -} +/* Helper functions */ /* - * Check for overflow of an increased size or capacity (generically, 'value'). - * 'diff' is how much greater newval should be compared to oldval. + * This helper function is specialized for appending the characters of a string + * literal to a vector. This could not be done generically since one must take + * care not to append the terminating '\0'. */ -template -inline bool -JSTempVector::checkOverflow(size_t newval, size_t oldval, size_t diff) const +template +bool +js_AppendLiteral(JSTempVector &v, const char (&array)[ArraySize]) { - size_t newbytes = newval * sizeof(T), - oldbytes = oldval * sizeof(T), - diffbytes = diff * sizeof(T); - bool ok = newbytes >= oldbytes && (newbytes - oldbytes) >= diffbytes; - if (!ok) - js_ReportAllocationOverflow(mCx); - return ok; + return v.append(array, array + ArraySize - 1); } -template -inline bool -JSTempVector::pushBack(const T &t) + +/* JSTempVector Implementation */ + +template +inline +JSTempVector::~JSTempVector() { ReentrancyGuard g(*this); - if (mEnd == mCapacity) { - /* reallocate, doubling size */ - size_t oldcap = capacity(); - size_t newcap = empty() ? 1 : oldcap * sGrowthFactor; - if (!checkOverflow(newcap, oldcap, 1) || - !Impl::growTo(*this, newcap)) - return false; + if (usingInlineStorage()) { + Impl::destroy(inlineBegin(), inlineEnd()); + } else { + Impl::destroy(heapBegin(), heapEnd()); + mCx->free(heapBegin()); } - JS_ASSERT(mEnd != mCapacity); - new(mEnd++) T(t); +} + +template +inline bool +JSTempVector::growHeapCapacityTo(size_t mincap) +{ + JS_ASSERT(mincap > heapCapacity()); + + /* Check for overflow in both CEILING_LOG2 and growTo. */ + if (mincap & JSUtils::MulOverflowMask<2 * sizeof(T)>::result) { + js_ReportAllocationOverflow(mCx); + return false; + } + + /* Round up to next power of 2. */ + size_t newcap; + JS_CEILING_LOG2(newcap, mincap); + JS_ASSERT(newcap < JSUtils::BitSize::result); + newcap = size_t(1) << newcap; + + return Impl::growTo(*this, newcap); +} + +template +inline bool +JSTempVector::convertToHeapStorage(size_t mincap) +{ + JS_ASSERT(mincap > sInlineCapacity); + + /* Check for overflow in both CEILING_LOG2 and malloc. */ + if (mincap & JSUtils::MulOverflowMask<2 * sizeof(T)>::result) { + js_ReportAllocationOverflow(mCx); + return false; + } + + /* Round up to next power of 2. */ + size_t newcap; + JS_CEILING_LOG2(newcap, mincap); + JS_ASSERT(newcap < 32); + newcap = 1u << newcap; + + /* Allocate buffer. */ + T *newbuf = reinterpret_cast(mCx->malloc(newcap * sizeof(T))); + if (!newbuf) + return false; + + /* Copy inline elements into heap buffer. */ + size_t size = inlineEnd() - inlineBegin(); + Impl::copyConstruct(newbuf, inlineBegin(), inlineEnd()); + Impl::destroy(inlineBegin(), inlineEnd()); + + /* Switch in heap buffer. */ + mSizeOrCapacity = newcap; /* marks us as !usingInlineStorage() */ + heapBegin() = newbuf; + heapEnd() = newbuf + size; return true; } -template +template +inline bool +JSTempVector::reserve(size_t request) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + if (request > sInlineCapacity) + return convertToHeapStorage(request); + } else { + if (request > heapCapacity()) + return growHeapCapacityTo(request); + } + return true; +} + +template +inline void +JSTempVector::shrinkBy(size_t incr) +{ + ReentrancyGuard g(*this); + JS_ASSERT(incr <= size()); + if (usingInlineStorage()) { + Impl::destroy(inlineEnd() - incr, inlineEnd()); + inlineSize() -= incr; + } else { + Impl::destroy(heapEnd() - incr, heapEnd()); + heapEnd() -= incr; + } +} + +template +inline bool +JSTempVector::growBy(size_t incr) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + size_t freespace = sInlineCapacity - inlineSize(); + if (incr <= freespace) { + T *newend = inlineEnd() + incr; + Impl::initialize(inlineEnd(), newend); + inlineSize() += incr; + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(inlineSize() + incr)) + return false; + } + else { + /* grow if needed */ + size_t freespace = heapCapacity() - heapSize(); + if (incr > freespace) { + if (!growHeapCapacityTo(heapSize() + incr)) + return false; + } + } + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapCapacity() - heapSize() >= incr); + T *newend = heapEnd() + incr; + Impl::initialize(heapEnd(), newend); + heapEnd() = newend; + return true; +} + +template +inline bool +JSTempVector::resize(size_t newsize) +{ + size_t cursize = size(); + if (newsize > cursize) + return growBy(newsize - cursize); + shrinkBy(cursize - newsize); + return true; +} + +template +inline void +JSTempVector::clear() +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + Impl::destroy(inlineBegin(), inlineEnd()); + inlineSize() = 0; + } + else { + Impl::destroy(heapBegin(), heapEnd()); + heapEnd() = heapBegin(); + } +} + +template +inline bool +JSTempVector::append(const T &t) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + if (inlineSize() < sInlineCapacity) { + new(inlineEnd()) T(t); + ++inlineSize(); + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(inlineSize() + 1)) + return false; + } else { + if (heapSize() == heapCapacity() && !growHeapCapacityTo(heapSize() + 1)) + return false; + } + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= 1); + new(heapEnd()++) T(t); + return true; +} + +template +inline bool +JSTempVector::appendN(const T &t, size_t needed) +{ + ReentrancyGuard g(*this); + if (usingInlineStorage()) { + size_t freespace = sInlineCapacity - inlineSize(); + if (needed <= freespace) { + Impl::copyConstructN(inlineEnd(), needed, t); + inlineSize() += needed; + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(inlineSize() + needed)) + return false; + } else { + size_t freespace = heapCapacity() - heapSize(); + if (needed > freespace && !growHeapCapacityTo(heapSize() + needed)) + return false; + } + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= needed); + Impl::copyConstructN(heapEnd(), needed, t); + heapEnd() += needed; + return true; +} + +template template inline bool -JSTempVector::pushBack(const U *begin, const U *end) +JSTempVector::append(const U *insBegin, const U *insEnd) { ReentrancyGuard g(*this); - size_t space = mCapacity - mEnd, needed = end - begin; - if (space < needed) { - /* reallocate, doubling size */ - size_t oldcap = capacity(); - size_t newcap = empty() ? needed : (needed + oldcap * sGrowthFactor); - if (!checkOverflow(newcap, oldcap, needed) || - !Impl::growTo(*this, newcap)) + size_t needed = insEnd - insBegin; + if (usingInlineStorage()) { + size_t freespace = sInlineCapacity - inlineSize(); + if (needed <= freespace) { + Impl::copyConstruct(inlineEnd(), insBegin, insEnd); + inlineSize() += needed; + JS_ASSERT(usingInlineStorage()); + return true; + } + if (!convertToHeapStorage(inlineSize() + needed)) + return false; + } else { + size_t freespace = heapCapacity() - heapSize(); + if (needed > freespace && !growHeapCapacityTo(heapSize() + needed)) return false; } - JS_ASSERT((mCapacity - mEnd) >= (end - begin)); - Impl::copyInitialize(mEnd, begin, end); - mEnd += needed; + + /* We are !usingInlineStorage(). Initialize new elements. */ + JS_ASSERT(heapSize() <= heapCapacity() && heapCapacity() - heapSize() >= needed); + Impl::copyConstruct(heapEnd(), insBegin, insEnd); + heapEnd() += needed; return true; } -template -inline T * -JSTempVector::extractRawBuffer() +template +template +inline bool +JSTempVector::append(const U *insBegin, size_t length) { - T *ret = mBegin; - mBegin = mEnd = mCapacity = 0; + return this->append(insBegin, insBegin + length); +} + +template +inline void +JSTempVector::popBack() +{ + ReentrancyGuard g(*this); + JS_ASSERT(!empty()); + if (usingInlineStorage()) { + --inlineSize(); + inlineEnd()->~T(); + } else { + --heapEnd(); + heapEnd()->~T(); + } +} + +template +inline T * +JSTempVector::extractRawBuffer() +{ + if (usingInlineStorage()) { + T *ret = reinterpret_cast(mCx->malloc(inlineSize() * sizeof(T))); + if (!ret) + return NULL; + Impl::copyConstruct(ret, inlineBegin(), inlineEnd()); + Impl::destroy(inlineBegin(), inlineEnd()); + inlineSize() = 0; + return ret; + } + + T *ret = heapBegin(); + mSizeOrCapacity = 0; /* marks us as !usingInlineStorage() */ return ret; } -template +template inline void -JSTempVector::replaceRawBuffer(T *p, size_t length) +JSTempVector::replaceRawBuffer(T *p, size_t length) { ReentrancyGuard g(*this); - Impl::destroy(mBegin, mEnd); - free(mBegin); - mBegin = p; - mCapacity = mEnd = mBegin + length; + + /* Destroy what we have. */ + if (usingInlineStorage()) { + Impl::destroy(inlineBegin(), inlineEnd()); + inlineSize() = 0; + } else { + Impl::destroy(heapBegin(), heapEnd()); + mCx->free(heapBegin()); + } + + /* Take in the new buffer. */ + if (length <= sInlineCapacity) { + /* + * (mSizeOrCapacity <= sInlineCapacity) means inline storage, so we MUST + * use inline storage, even though p might otherwise be acceptable. + */ + mSizeOrCapacity = length; /* marks us as usingInlineStorage() */ + Impl::copyConstruct(inlineBegin(), p, p + length); + Impl::destroy(p, p + length); + mCx->free(p); + } else { + mSizeOrCapacity = length; /* marks us as !usingInlineStorage() */ + heapBegin() = p; + heapEnd() = heapBegin() + length; + } } #endif /* jsvector_h_ */