Bug 587257 - Make Array.prototype.join faster. r=lw
This commit is contained in:
@@ -106,6 +106,7 @@
|
||||
#include "jsatominlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
@@ -1254,37 +1255,88 @@ array_toSource(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef HashSet<JSObject *> ObjSet;
|
||||
|
||||
class AutoArrayCycleDetector
|
||||
{
|
||||
public:
|
||||
AutoArrayCycleDetector(JSContext *cx, JSObject *obj JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: context(cx), object(obj), genBefore(0),
|
||||
hashPointer(context->busyArrays.lookupForAdd(object)), cycle(!!hashPointer)
|
||||
{
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
~AutoArrayCycleDetector() {
|
||||
JS_ASSERT(context);
|
||||
if (!cycle && hashPointer) {
|
||||
if (genBefore == context->busyArrays.generation())
|
||||
context->busyArrays.remove(hashPointer);
|
||||
else
|
||||
context->busyArrays.remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
bool foundCycle() {
|
||||
return cycle;
|
||||
}
|
||||
|
||||
bool update() {
|
||||
JS_ASSERT(!hashPointer);
|
||||
if (!context->busyArrays.add(hashPointer, object))
|
||||
return false;
|
||||
genBefore = context->busyArrays.generation();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
JSContext *context;
|
||||
JSObject *object;
|
||||
uint32 genBefore;
|
||||
ObjSet::AddPtr hashPointer;
|
||||
JSBool cycle;
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
static inline JSBool
|
||||
GetElementCharacters(JSContext *cx, JSObject *obj, JSBool locale, JSCharBuffer *cb, Value *rval)
|
||||
{
|
||||
if (locale) {
|
||||
/* Work on obj.toLocalString() instead. */
|
||||
JSObject *robj;
|
||||
|
||||
if (!js_ValueToObjectOrNull(cx, *rval, &robj))
|
||||
return false;
|
||||
rval->setObjectOrNull(robj);
|
||||
JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom;
|
||||
if (!js_TryMethod(cx, robj, atom, 0, NULL, rval))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* avoid calling js_ValueToCharBuffer if possible */
|
||||
if (rval->isString())
|
||||
return js_StringValueToCharBuffer(cx, *rval, *cb);
|
||||
|
||||
return js_ValueToCharBuffer(cx, *rval, *cb);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
||||
JSString *sepstr, Value *rval)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
/*
|
||||
* Use HashTable entry as the cycle indicator. On first visit, create the
|
||||
* entry, and, when leaving, remove the entry.
|
||||
*/
|
||||
typedef js::HashSet<JSObject *> ObjSet;
|
||||
ObjSet::AddPtr hashp = cx->busyArrays.lookupForAdd(obj);
|
||||
uint32 genBefore;
|
||||
if (!hashp) {
|
||||
/* Not in hash table, so not a cycle. */
|
||||
if (!cx->busyArrays.add(hashp, obj)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
genBefore = cx->busyArrays.generation();
|
||||
} else {
|
||||
/* Cycle, so return empty string. */
|
||||
AutoArrayCycleDetector detector(cx, obj);
|
||||
|
||||
if (detector.foundCycle()) {
|
||||
rval->setString(ATOM_TO_STRING(cx->runtime->atomState.emptyAtom));
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoObjectRooter tvr(cx, obj);
|
||||
if (!detector.update())
|
||||
return false;
|
||||
|
||||
/* After this point, all paths exit through the 'out' label. */
|
||||
MUST_FLOW_THROUGH("out");
|
||||
bool ok = false;
|
||||
AutoObjectRooter tvr(cx, obj);
|
||||
|
||||
/* Get characters to use for the separator. */
|
||||
static const jschar comma = ',';
|
||||
@@ -1305,53 +1357,41 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale,
|
||||
|
||||
jsuint length;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
goto out;
|
||||
return false;
|
||||
|
||||
for (jsuint index = 0; index < length; index++) {
|
||||
/* Use rval to locally root each element value. */
|
||||
JSBool hole;
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
|
||||
!GetArrayElement(cx, obj, index, &hole, rval)) {
|
||||
goto out;
|
||||
/* avoid extra checks inside the loop if possible */
|
||||
if (seplen == 0 && obj->isDenseArray()) {
|
||||
jsuint len = JS_MIN(length, obj->getDenseArrayCapacity());
|
||||
for (Value *vp = obj->getDenseArrayElements(), *end = vp + len; vp != end; ++vp) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
*rval = *vp;
|
||||
if (!rval->isMagic(JS_ARRAY_HOLE) && !rval->isNullOrUndefined()) {
|
||||
if (!GetElementCharacters(cx, obj, locale, &cb, rval))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JSBool hole;
|
||||
for (jsuint index = 0; index < length; index++) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetArrayElement(cx, obj, index, &hole, rval))
|
||||
return false;
|
||||
|
||||
/* Get element's character string. */
|
||||
if (!(hole || rval->isNullOrUndefined())) {
|
||||
if (locale) {
|
||||
/* Work on obj.toLocalString() instead. */
|
||||
JSObject *robj;
|
||||
|
||||
if (!js_ValueToObjectOrNull(cx, *rval, &robj))
|
||||
goto out;
|
||||
rval->setObjectOrNull(robj);
|
||||
JSAtom *atom = cx->runtime->atomState.toLocaleStringAtom;
|
||||
if (!js_TryMethod(cx, robj, atom, 0, NULL, rval))
|
||||
goto out;
|
||||
if (!hole && !rval->isNullOrUndefined()) {
|
||||
if (!GetElementCharacters(cx, obj, locale, &cb, rval))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!js_ValueToCharBuffer(cx, *rval, cb))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Append the separator. */
|
||||
if (index + 1 != length) {
|
||||
if (!cb.append(sep, seplen))
|
||||
goto out;
|
||||
/* Append the separator. */
|
||||
if (index + 1 != length) {
|
||||
if (!cb.append(sep, seplen))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalize the buffer. */
|
||||
if (!BufferToString(cx, cb, rval))
|
||||
goto out;
|
||||
|
||||
ok = true;
|
||||
|
||||
out:
|
||||
if (genBefore == cx->busyArrays.generation())
|
||||
cx->busyArrays.remove(hashp);
|
||||
else
|
||||
cx->busyArrays.remove(obj);
|
||||
return ok;
|
||||
return BufferToString(cx, cb, rval);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.2. NB: The algorithm here differs from the one in ES3. */
|
||||
@@ -2632,7 +2672,7 @@ array_indexOfHelper(JSContext *cx, JSBool isLast, uintN argc, Value *vp)
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (!hole && StrictlyEqual(cx, *vp, tosearch)) {
|
||||
vp->setNumber(i);
|
||||
vp->setNumber(i);
|
||||
return JS_TRUE;
|
||||
}
|
||||
if (i == stop)
|
||||
|
||||
Reference in New Issue
Block a user