Merge MC -> JM
This commit is contained in:
@@ -362,22 +362,9 @@ JSObject::arrayGetOwnDataElement(JSContext *cx, size_t i, Value *vp)
|
||||
* to JSVAL_VOID. This function assumes that the location pointed by vp is
|
||||
* properly rooted and can be used as GC-protected storage for temporaries.
|
||||
*/
|
||||
static JSBool
|
||||
GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp)
|
||||
static inline JSBool
|
||||
DoGetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp)
|
||||
{
|
||||
JS_ASSERT(index >= 0);
|
||||
if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() &&
|
||||
!(*vp = obj->getDenseArrayElement(uint32(index))).isMagic(JS_ARRAY_HOLE)) {
|
||||
*hole = JS_FALSE;
|
||||
return JS_TRUE;
|
||||
}
|
||||
if (obj->isArguments()) {
|
||||
if (obj->asArguments()->getElement(uint32(index), vp)) {
|
||||
*hole = JS_FALSE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
AutoIdRooter idr(cx);
|
||||
|
||||
*hole = JS_FALSE;
|
||||
@@ -393,8 +380,8 @@ GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp
|
||||
if (!obj->lookupGeneric(cx, idr.id(), &obj2, &prop))
|
||||
return JS_FALSE;
|
||||
if (!prop) {
|
||||
*hole = JS_TRUE;
|
||||
vp->setUndefined();
|
||||
*hole = JS_TRUE;
|
||||
} else {
|
||||
if (!obj->getGeneric(cx, idr.id(), vp))
|
||||
return JS_FALSE;
|
||||
@@ -403,6 +390,40 @@ GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static inline JSBool
|
||||
DoGetElement(JSContext *cx, JSObject *obj, uint32 index, JSBool *hole, Value *vp)
|
||||
{
|
||||
bool present;
|
||||
if (!obj->getElementIfPresent(cx, obj, index, vp, &present))
|
||||
return false;
|
||||
|
||||
*hole = !present;
|
||||
if (*hole)
|
||||
vp->setUndefined();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename IndexType>
|
||||
static JSBool
|
||||
GetElement(JSContext *cx, JSObject *obj, IndexType index, JSBool *hole, Value *vp)
|
||||
{
|
||||
JS_ASSERT(index >= 0);
|
||||
if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() &&
|
||||
!(*vp = obj->getDenseArrayElement(uint32(index))).isMagic(JS_ARRAY_HOLE)) {
|
||||
*hole = JS_FALSE;
|
||||
return JS_TRUE;
|
||||
}
|
||||
if (obj->isArguments()) {
|
||||
if (obj->asArguments()->getElement(uint32(index), vp)) {
|
||||
*hole = JS_FALSE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return DoGetElement(cx, obj, index, hole, vp);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
static bool
|
||||
@@ -796,26 +817,13 @@ array_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Valu
|
||||
|
||||
if (!js_IdIsIndex(id, &i) || i >= obj->getDenseArrayInitializedLength() ||
|
||||
obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
const Shape *shape;
|
||||
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto) {
|
||||
vp->setUndefined();
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
vp->setUndefined();
|
||||
if (!LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags, &obj2, &prop))
|
||||
return JS_FALSE;
|
||||
|
||||
if (prop && obj2->isNative()) {
|
||||
shape = (const Shape *) prop;
|
||||
if (!js_NativeGet(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, vp))
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
return proto->getGeneric(cx, receiver, id, vp);
|
||||
}
|
||||
|
||||
*vp = obj->getDenseArrayElement(i);
|
||||
@@ -839,7 +847,7 @@ array_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index,
|
||||
if (!obj->isDenseArray())
|
||||
return js_GetElement(cx, obj, index, vp);
|
||||
|
||||
if (index < obj->getDenseArrayCapacity() &&
|
||||
if (index < obj->getDenseArrayInitializedLength() &&
|
||||
!obj->getDenseArrayElement(index).isMagic(JS_ARRAY_HOLE))
|
||||
{
|
||||
*vp = obj->getDenseArrayElement(index);
|
||||
@@ -852,22 +860,7 @@ array_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32 index,
|
||||
return true;
|
||||
}
|
||||
|
||||
vp->setUndefined();
|
||||
|
||||
jsid id;
|
||||
if (!IndexToId(cx, index, &id))
|
||||
return false;
|
||||
|
||||
JSObject *obj2;
|
||||
JSProperty *prop;
|
||||
if (!LookupPropertyWithFlags(cx, proto, id, cx->resolveFlags, &obj2, &prop))
|
||||
return false;
|
||||
|
||||
if (!prop || !obj2->isNative())
|
||||
return true;
|
||||
|
||||
const Shape *shape = (const Shape *) prop;
|
||||
return js_NativeGet(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, vp);
|
||||
return proto->getElement(cx, receiver, index, vp);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@@ -1272,6 +1265,8 @@ Class js::ArrayClass = {
|
||||
array_getGeneric,
|
||||
array_getProperty,
|
||||
array_getElement,
|
||||
NULL, /* getElementIfPresent, because this is hard for now for
|
||||
slow arrays */
|
||||
array_getSpecial,
|
||||
array_setGeneric,
|
||||
array_setProperty,
|
||||
@@ -2167,8 +2162,7 @@ sort_compare(void *arg, const void *a, const void *b, int *result)
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 2, &ag))
|
||||
return JS_FALSE;
|
||||
|
||||
ag.calleeHasBeenReset();
|
||||
ag.calleev() = ca->fval;
|
||||
ag.setCallee(ca->fval);
|
||||
ag.thisv() = UndefinedValue();
|
||||
ag[0] = *av;
|
||||
ag[1] = *bv;
|
||||
@@ -2670,7 +2664,7 @@ js::array_shift(JSContext *cx, uintN argc, Value *vp)
|
||||
}
|
||||
|
||||
JSBool hole;
|
||||
if (!GetElement(cx, obj, 0, &hole, &args.rval()))
|
||||
if (!GetElement(cx, obj, 0u, &hole, &args.rval()))
|
||||
return JS_FALSE;
|
||||
|
||||
/* Slide down the array above the first element. */
|
||||
@@ -3287,256 +3281,419 @@ array_lastIndexOf(JSContext *cx, uintN argc, Value *vp)
|
||||
return array_indexOfHelper(cx, LastIndexOf, args);
|
||||
}
|
||||
|
||||
/* Order is important; extras that take a predicate funarg must follow MAP. */
|
||||
typedef enum ArrayExtraMode {
|
||||
FOREACH,
|
||||
REDUCE,
|
||||
REDUCE_RIGHT,
|
||||
MAP,
|
||||
FILTER,
|
||||
SOME,
|
||||
EVERY
|
||||
} ArrayExtraMode;
|
||||
|
||||
#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
|
||||
|
||||
static JSBool
|
||||
array_extra(JSContext *cx, ArrayExtraMode mode, CallArgs &args)
|
||||
/* ECMA 15.4.4.16-15.4.4.18. */
|
||||
class ArrayForEachBehavior
|
||||
{
|
||||
public:
|
||||
static bool shouldExit(Value &callval, Value *rval) { return false; }
|
||||
static Value lateExitValue() { return UndefinedValue(); }
|
||||
};
|
||||
|
||||
class ArrayEveryBehavior
|
||||
{
|
||||
public:
|
||||
static bool shouldExit(Value &callval, Value *rval)
|
||||
{
|
||||
if (!js_ValueToBoolean(callval)) {
|
||||
*rval = BooleanValue(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static Value lateExitValue() { return BooleanValue(true); }
|
||||
};
|
||||
|
||||
class ArraySomeBehavior
|
||||
{
|
||||
public:
|
||||
static bool shouldExit(Value &callval, Value *rval)
|
||||
{
|
||||
if (js_ValueToBoolean(callval)) {
|
||||
*rval = BooleanValue(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static Value lateExitValue() { return BooleanValue(false); }
|
||||
};
|
||||
|
||||
template <class Behavior>
|
||||
static inline bool
|
||||
array_readonlyCommon(JSContext *cx, CallArgs &args)
|
||||
{
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
jsuint length;
|
||||
if (!js_GetLengthProperty(cx, obj, &length))
|
||||
return JS_FALSE;
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* First, get or compute our callee, so that we error out consistently
|
||||
* when passed a non-callable object.
|
||||
*/
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return JS_FALSE;
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Set our initial return condition, used for zero-length array cases
|
||||
* (and pre-size our map return to match our known length, for all cases).
|
||||
*/
|
||||
jsuint newlen;
|
||||
JSObject *newarr;
|
||||
TypeObject *newtype = NULL;
|
||||
#ifdef __GNUC__ /* quell GCC overwarning */
|
||||
newlen = 0;
|
||||
newarr = NULL;
|
||||
#endif
|
||||
jsuint start = 0, end = length;
|
||||
jsint step = 1;
|
||||
/* Step 5. */
|
||||
Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
|
||||
|
||||
switch (mode) {
|
||||
case REDUCE_RIGHT:
|
||||
start = length - 1, end = -1, step = -1;
|
||||
/* FALL THROUGH */
|
||||
case REDUCE:
|
||||
if (length == 0 && args.length() == 1) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (args.length() >= 2) {
|
||||
args.rval() = args[1];
|
||||
} else {
|
||||
JSBool hole;
|
||||
do {
|
||||
if (!GetElement(cx, obj, start, &hole, &args.rval()))
|
||||
return JS_FALSE;
|
||||
start += step;
|
||||
} while (hole && start != end);
|
||||
/* Step 6. */
|
||||
uint32 k = 0;
|
||||
|
||||
if (hole && start == end) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MAP:
|
||||
case FILTER:
|
||||
newlen = (mode == MAP) ? length : 0;
|
||||
newarr = NewDenseAllocatedArray(cx, newlen);
|
||||
if (!newarr)
|
||||
return JS_FALSE;
|
||||
newtype = GetTypeCallerInitObject(cx, JSProto_Array);
|
||||
if (!newtype)
|
||||
return JS_FALSE;
|
||||
newarr->setType(newtype);
|
||||
args.rval().setObject(*newarr);
|
||||
break;
|
||||
case SOME:
|
||||
args.rval().setBoolean(false);
|
||||
break;
|
||||
case EVERY:
|
||||
args.rval().setBoolean(true);
|
||||
break;
|
||||
case FOREACH:
|
||||
args.rval().setUndefined();
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
return JS_TRUE;
|
||||
|
||||
Value thisv = (args.length() > 1 && !REDUCE_MODE(mode)) ? args[1] : UndefinedValue();
|
||||
|
||||
/*
|
||||
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE
|
||||
* requires 4 args (accum, value, index, array).
|
||||
*/
|
||||
uintN agArgc = 3 + REDUCE_MODE(mode);
|
||||
|
||||
MUST_FLOW_THROUGH("out");
|
||||
JSBool ok = JS_TRUE;
|
||||
JSBool cond;
|
||||
|
||||
Value objv = ObjectValue(*obj);
|
||||
AutoValueRooter tvr(cx);
|
||||
/* Step 7. */
|
||||
InvokeArgsGuard ag;
|
||||
for (jsuint i = start; i != end; i += step) {
|
||||
JSBool hole;
|
||||
ok = JS_CHECK_OPERATION_LIMIT(cx) &&
|
||||
GetElement(cx, obj, i, &hole, tvr.addr());
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (hole)
|
||||
continue;
|
||||
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, agArgc, &ag))
|
||||
while (k < len) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Push callable and 'this', then args. We must do this for every
|
||||
* iteration around the loop since Invoke clobbers its arguments.
|
||||
*/
|
||||
ag.calleeHasBeenReset();
|
||||
ag.calleev() = ObjectValue(*callable);
|
||||
ag.thisv() = thisv;
|
||||
uintN argi = 0;
|
||||
if (REDUCE_MODE(mode))
|
||||
ag[argi++] = args.rval();
|
||||
ag[argi++] = tvr.value();
|
||||
ag[argi++] = Int32Value(i);
|
||||
ag[argi] = objv;
|
||||
/* Step a, b, and c.i. */
|
||||
Value kValue;
|
||||
JSBool kNotPresent;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Do the call. */
|
||||
ok = Invoke(cx, ag);
|
||||
if (!ok)
|
||||
break;
|
||||
/* Step c.ii-iii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
|
||||
return false;
|
||||
ag.setCallee(ObjectValue(*callable));
|
||||
ag.thisv() = thisv;
|
||||
ag[0] = kValue;
|
||||
ag[1] = NumberValue(k);
|
||||
ag[2] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
|
||||
const Value &rval = ag.rval();
|
||||
|
||||
if (mode > MAP)
|
||||
cond = js_ValueToBoolean(rval);
|
||||
#ifdef __GNUC__ /* quell GCC overwarning */
|
||||
else
|
||||
cond = JS_FALSE;
|
||||
#endif
|
||||
|
||||
switch (mode) {
|
||||
case FOREACH:
|
||||
break;
|
||||
case REDUCE:
|
||||
case REDUCE_RIGHT:
|
||||
args.rval() = rval;
|
||||
break;
|
||||
case MAP:
|
||||
if (!ok)
|
||||
goto out;
|
||||
ok = SetArrayElement(cx, newarr, i, rval);
|
||||
if (!ok)
|
||||
goto out;
|
||||
break;
|
||||
case FILTER:
|
||||
if (!cond)
|
||||
break;
|
||||
/* The element passed the filter, so push it onto our result. */
|
||||
if (!ok)
|
||||
goto out;
|
||||
ok = SetArrayElement(cx, newarr, newlen++, tvr.value());
|
||||
if (!ok)
|
||||
goto out;
|
||||
break;
|
||||
case SOME:
|
||||
if (cond) {
|
||||
args.rval().setBoolean(true);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case EVERY:
|
||||
if (!cond) {
|
||||
args.rval().setBoolean(false);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
if (Behavior::shouldExit(ag.rval(), &args.rval()))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k++;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ok && mode == FILTER)
|
||||
ok = js_SetLengthProperty(cx, newarr, newlen);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_forEach(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, FOREACH, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_map(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, MAP, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_reduce(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, REDUCE, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_reduceRight(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, REDUCE_RIGHT, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_filter(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, FILTER, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
array_some(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, SOME, args);
|
||||
}
|
||||
|
||||
/* Step 8. */
|
||||
args.rval() = Behavior::lateExitValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.16. */
|
||||
static JSBool
|
||||
array_every(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_extra(cx, EVERY, args);
|
||||
return array_readonlyCommon<ArrayEveryBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.17. */
|
||||
static JSBool
|
||||
array_some(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_readonlyCommon<ArraySomeBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.18. */
|
||||
static JSBool
|
||||
array_forEach(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_readonlyCommon<ArrayForEachBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.19. */
|
||||
static JSBool
|
||||
array_map(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return false;
|
||||
|
||||
/* Step 5. */
|
||||
Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
|
||||
|
||||
/* Step 6. */
|
||||
JSObject *arr = NewDenseAllocatedArray(cx, len);
|
||||
if (!arr)
|
||||
return false;
|
||||
TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
|
||||
if (!newtype)
|
||||
return false;
|
||||
arr->setType(newtype);
|
||||
|
||||
/* Step 7. */
|
||||
uint32 k = 0;
|
||||
|
||||
/* Step 8. */
|
||||
InvokeArgsGuard ag;
|
||||
while (k < len) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/* Step a, b, and c.i. */
|
||||
JSBool kNotPresent;
|
||||
Value kValue;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Step c.ii-iii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
|
||||
return false;
|
||||
ag.setCallee(ObjectValue(*callable));
|
||||
ag.thisv() = thisv;
|
||||
ag[0] = kValue;
|
||||
ag[1] = NumberValue(k);
|
||||
ag[2] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
if(!SetArrayElement(cx, arr, k, ag.rval()))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k++;
|
||||
}
|
||||
|
||||
/* Step 9. */
|
||||
args.rval().setObject(*arr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.20. */
|
||||
static JSBool
|
||||
array_filter(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return false;
|
||||
|
||||
/* Step 5. */
|
||||
Value thisv = args.length() >= 2 ? args[1] : UndefinedValue();
|
||||
|
||||
/* Step 6. */
|
||||
JSObject *arr = NewDenseAllocatedArray(cx, 0);
|
||||
if (!arr)
|
||||
return false;
|
||||
TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array);
|
||||
if (!newtype)
|
||||
return false;
|
||||
arr->setType(newtype);
|
||||
|
||||
/* Step 7. */
|
||||
uint32 k = 0;
|
||||
|
||||
/* Step 8. */
|
||||
uint32 to = 0;
|
||||
|
||||
/* Step 9. */
|
||||
InvokeArgsGuard ag;
|
||||
while (k < len) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/* Step a, b, and c.i. */
|
||||
JSBool kNotPresent;
|
||||
Value kValue;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Step c.ii-iii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag))
|
||||
return false;
|
||||
ag.setCallee(ObjectValue(*callable));
|
||||
ag.thisv() = thisv;
|
||||
ag[0] = kValue;
|
||||
ag[1] = NumberValue(k);
|
||||
ag[2] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
|
||||
if (js_ValueToBoolean(ag.rval())) {
|
||||
if(!SetArrayElement(cx, arr, to, kValue))
|
||||
return false;
|
||||
to++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k++;
|
||||
}
|
||||
|
||||
/* Step 10. */
|
||||
args.rval().setObject(*arr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.21-15.4.4.22. */
|
||||
class ArrayReduceBehavior
|
||||
{
|
||||
public:
|
||||
static void initialize(uint32 len, uint32 *start, uint32 *end, int32 *step)
|
||||
{
|
||||
*start = 0;
|
||||
*step = 1;
|
||||
*end = len;
|
||||
}
|
||||
};
|
||||
|
||||
class ArrayReduceRightBehavior
|
||||
{
|
||||
public:
|
||||
static void initialize(uint32 len, uint32 *start, uint32 *end, int32 *step)
|
||||
{
|
||||
*start = len - 1;
|
||||
*step = -1;
|
||||
/*
|
||||
* We rely on (well defined) unsigned integer underflow to check our
|
||||
* end condition after visiting the full range (including 0).
|
||||
*/
|
||||
*end = (uint32)-1;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Behavior>
|
||||
static inline bool
|
||||
array_reduceCommon(JSContext *cx, CallArgs &args)
|
||||
{
|
||||
/* Step 1. */
|
||||
JSObject *obj = ToObject(cx, &args.thisv());
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
/* Step 2-3. */
|
||||
uint32 len;
|
||||
if (!js_GetLengthProperty(cx, obj, &len))
|
||||
return false;
|
||||
|
||||
/* Step 4. */
|
||||
if (args.length() == 0) {
|
||||
js_ReportMissingArg(cx, args.calleev(), 0);
|
||||
return false;
|
||||
}
|
||||
JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
|
||||
if (!callable)
|
||||
return false;
|
||||
|
||||
/* Step 5. */
|
||||
if (len == 0 && args.length() < 2) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Step 6. */
|
||||
uint32 k, end;
|
||||
int32 step;
|
||||
Behavior::initialize(len, &k, &end, &step);
|
||||
|
||||
/* Step 7-8. */
|
||||
Value accumulator;
|
||||
if (args.length() >= 2) {
|
||||
accumulator = args[1];
|
||||
} else {
|
||||
JSBool kNotPresent = true;
|
||||
while (kNotPresent && k != end) {
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &accumulator))
|
||||
return false;
|
||||
k += step;
|
||||
}
|
||||
if (kNotPresent) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 9. */
|
||||
InvokeArgsGuard ag;
|
||||
while (k != end) {
|
||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||
return false;
|
||||
|
||||
/* Step a, b, and c.i. */
|
||||
JSBool kNotPresent;
|
||||
Value kValue;
|
||||
if (!GetElement(cx, obj, k, &kNotPresent, &kValue))
|
||||
return false;
|
||||
|
||||
/* Step c.ii. */
|
||||
if (!kNotPresent) {
|
||||
if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 4, &ag))
|
||||
return false;
|
||||
ag.setCallee(ObjectValue(*callable));
|
||||
ag.thisv() = UndefinedValue();
|
||||
ag[0] = accumulator;
|
||||
ag[1] = kValue;
|
||||
ag[2] = NumberValue(k);
|
||||
ag[3] = ObjectValue(*obj);
|
||||
if (!Invoke(cx, ag))
|
||||
return false;
|
||||
accumulator = ag.rval();
|
||||
}
|
||||
|
||||
/* Step d. */
|
||||
k += step;
|
||||
}
|
||||
|
||||
/* Step 10. */
|
||||
args.rval() = accumulator;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.21. */
|
||||
static JSBool
|
||||
array_reduce(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_reduceCommon<ArrayReduceBehavior>(cx, args);
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.22. */
|
||||
static JSBool
|
||||
array_reduceRight(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return array_reduceCommon<ArrayReduceRightBehavior>(cx, args);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
||||
Reference in New Issue
Block a user