Bug 503952 - JSStringBuffer/JSCharBuffer with JSTempVector, part 1. r=jwalden

This commit is contained in:
Luke Wagner 2009-08-07 20:09:11 -07:00
Родитель aaa22d1c75
Коммит e94edc5f03
11 изменённых файлов: 761 добавлений и 338 удалений

Просмотреть файл

@ -1331,58 +1331,51 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
}
/* Transfer ownership of buffer to returned string. */
static JSBool
BufferToString(JSContext *cx, JSTempVector<jschar> &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<jschar> 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<jschar> 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;

Просмотреть файл

@ -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,

Просмотреть файл

@ -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<jschar> &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

Просмотреть файл

@ -81,7 +81,7 @@ extern JSString *
js_BooleanToString(JSContext *cx, JSBool b);
extern JSBool
js_BooleanToStringBuffer(JSContext *cx, JSBool b, JSTempVector<jschar> &buf);
js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharVector &buf);
extern JSBool
js_ValueToBoolean(jsval v);

Просмотреть файл

@ -862,7 +862,7 @@ js_NumberToString(JSContext *cx, jsdouble d)
}
JSBool JS_FASTCALL
js_NumberValueToStringBuffer(JSContext *cx, jsval v, JSTempVector<jschar> &buf)
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &buf)
{
/* Convert to C-string. */
static const size_t arrSize = DTOSTR_STANDARD_BUFFER_SIZE;

Просмотреть файл

@ -194,7 +194,7 @@ js_NumberToString(JSContext *cx, jsdouble d);
* append to the given buffer.
*/
extern JSBool JS_FASTCALL
js_NumberValueToStringBuffer(JSContext *, jsval, JSTempVector<jschar> &);
js_NumberValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb);
/*
* Convert a value to a number. On exit JSVAL_IS_NULL(*vp) iff there was an

Просмотреть файл

@ -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 T> class JSTempVector;
template <class T, size_t MinInlineCapacity = 0> class JSTempVector;
/* Common JSTempVector instantiations: */
typedef JSTempVector<jschar, 32> JSCharVector;
}
#endif /* __cplusplus */

Просмотреть файл

@ -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))

Просмотреть файл

@ -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<jschar> &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<jschar> &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<jschar> &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));

Просмотреть файл

@ -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<jschar> &);
js_ValueToCharBuffer(JSContext *cx, jsval v, JSCharVector &cb);
/*
* Convert a value to its source expression, returning null after reporting

Просмотреть файл

@ -40,29 +40,74 @@
#ifndef jsvector_h_
#define jsvector_h_
#include "jscntxt.h"
#include <string.h>
#include <new>
#include "jsbit.h"
/* Library of template meta-programs for use in the C++ JS data-structures. */
namespace JSUtils {
/* Statically compute min/max. */
template <size_t i, size_t j> struct min {
static const size_t result = i < j ? i : j;
};
template <size_t i, size_t j> struct max {
static const size_t result = i > j ? i : j;
};
/* Statically compute floor(log2(i)). */
template <size_t i> struct FloorLog2 {
static const size_t result = 1 + FloorLog2<i / 2>::result;
};
template <> struct FloorLog2<0> { /* Error */ };
template <> struct FloorLog2<1> { static const size_t result = 0; };
/* Statically compute ceiling(log2(i)). */
template <size_t i> struct CeilingLog2 {
static const size_t result = FloorLog2<2 * i - 1>::result;
};
/* Statically compute the number of bits in the given unsigned type. */
template <class T> 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 <size_t N> struct MulOverflowMask {
static const size_t result =
~((1u << (BitSize<size_t>::result - CeilingLog2<N>::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 <class T> struct IsPodType { static const bool result = false; };
template <> struct IsPodType<char> { static const bool result = true; };
template <> struct IsPodType<int> { static const bool result = true; };
template <> struct IsPodType<short> { static const bool result = true; };
template <> struct IsPodType<long> { static const bool result = true; };
template <> struct IsPodType<float> { static const bool result = true; };
template <> struct IsPodType<double> { static const bool result = true; };
template <> struct IsPodType<jschar> { static const bool result = true; };
template <class T> struct IsPodType { static const bool result = false; };
template <> struct IsPodType<char> { static const bool result = true; };
template <> struct IsPodType<signed char> { static const bool result = true; };
template <> struct IsPodType<unsigned char> { static const bool result = true; };
template <> struct IsPodType<short> { static const bool result = true; };
template <> struct IsPodType<unsigned short> { static const bool result = true; };
template <> struct IsPodType<int> { static const bool result = true; };
template <> struct IsPodType<unsigned int> { static const bool result = true; };
template <> struct IsPodType<long> { static const bool result = true; };
template <> struct IsPodType<unsigned long> { static const bool result = true; };
template <> struct IsPodType<float> { static const bool result = true; };
template <> struct IsPodType<double> { 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 <class T, bool IsPod>
template <class T, size_t N, bool IsPod>
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 <class U>
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<T> &vec, size_t newcap) {
size_t bytes = sizeof(T) * newcap;
T *newbuf = reinterpret_cast<T *>(malloc(bytes));
if (!newbuf) {
js_ReportOutOfMemory(vec.mCx);
template <class U>
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<T> &v, size_t newcap) {
T *newbuf = reinterpret_cast<T *>(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 <class T>
struct JSTempVectorImpl<T, true>
template <class T, size_t N>
struct JSTempVectorImpl<T, N, true>
{
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 <class U>
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<T> &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<T,N> &v, size_t newcap) {
JS_ASSERT(!v.usingInlineStorage());
size_t bytes = sizeof(T) * newcap;
T *newbuf = reinterpret_cast<T *>(realloc(vec.mBegin, bytes));
if (!newbuf) {
js_ReportOutOfMemory(vec.mCx);
T *newbuf = reinterpret_cast<T *>(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<T, true>
/*
* 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 <class T>
template <class T, size_t N>
class JSTempVector
{
/* utilities */
typedef JSTempVectorImpl<T, N, JSUtils::IsPodType<T>::result> Impl;
friend struct JSTempVectorImpl<T, N, JSUtils::IsPodType<T>::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<JSUtils::max<N, sizeof(BufferPtrs) / sizeof(T)>::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 <class U> bool pushBack(const U *begin, const U *end);
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);
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<T, IsPodType<T>::result> Impl;
friend struct JSTempVectorImpl<T, IsPodType<T>::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 <class T>
inline
JSTempVector<T>::~JSTempVector()
{
ReentrancyGuard g(*this);
Impl::destroy(mBegin, mEnd);
free(mBegin);
}
template <class T>
inline bool
JSTempVector<T>::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 <class T>
inline bool
JSTempVector<T>::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 <class T>
inline void
JSTempVector<T>::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 <class T>
inline bool
JSTempVector<T>::checkOverflow(size_t newval, size_t oldval, size_t diff) const
template <class T, size_t N, size_t ArraySize>
bool
js_AppendLiteral(JSTempVector<T,N> &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 <class T>
inline bool
JSTempVector<T>::pushBack(const T &t)
/* JSTempVector Implementation */
template <class T, size_t N>
inline
JSTempVector<T,N>::~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 <class T, size_t N>
inline bool
JSTempVector<T,N>::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<size_t>::result);
newcap = size_t(1) << newcap;
return Impl::growTo(*this, newcap);
}
template <class T, size_t N>
inline bool
JSTempVector<T,N>::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<T *>(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 <class T>
template <class T, size_t N>
inline bool
JSTempVector<T,N>::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 <class T, size_t N>
inline void
JSTempVector<T,N>::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 <class T, size_t N>
inline bool
JSTempVector<T,N>::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 <class T, size_t N>
inline bool
JSTempVector<T,N>::resize(size_t newsize)
{
size_t cursize = size();
if (newsize > cursize)
return growBy(newsize - cursize);
shrinkBy(cursize - newsize);
return true;
}
template <class T, size_t N>
inline void
JSTempVector<T,N>::clear()
{
ReentrancyGuard g(*this);
if (usingInlineStorage()) {
Impl::destroy(inlineBegin(), inlineEnd());
inlineSize() = 0;
}
else {
Impl::destroy(heapBegin(), heapEnd());
heapEnd() = heapBegin();
}
}
template <class T, size_t N>
inline bool
JSTempVector<T,N>::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 <class T, size_t N>
inline bool
JSTempVector<T,N>::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 <class T, size_t N>
template <class U>
inline bool
JSTempVector<T>::pushBack(const U *begin, const U *end)
JSTempVector<T,N>::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 <class T>
inline T *
JSTempVector<T>::extractRawBuffer()
template <class T, size_t N>
template <class U>
inline bool
JSTempVector<T,N>::append(const U *insBegin, size_t length)
{
T *ret = mBegin;
mBegin = mEnd = mCapacity = 0;
return this->append(insBegin, insBegin + length);
}
template <class T, size_t N>
inline void
JSTempVector<T,N>::popBack()
{
ReentrancyGuard g(*this);
JS_ASSERT(!empty());
if (usingInlineStorage()) {
--inlineSize();
inlineEnd()->~T();
} else {
--heapEnd();
heapEnd()->~T();
}
}
template <class T, size_t N>
inline T *
JSTempVector<T,N>::extractRawBuffer()
{
if (usingInlineStorage()) {
T *ret = reinterpret_cast<T *>(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 <class T>
template <class T, size_t N>
inline void
JSTempVector<T>::replaceRawBuffer(T *p, size_t length)
JSTempVector<T,N>::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_ */